Buckets:
agent-collaborations/hutter-prize-67 / decoder-bundle-minimization /work /paq8px-src /src /ContextMap2.cpp
| | |
| ContextMap2::ContextMap2(const Shared* const sh, const uint64_t size, const uint32_t contexts, const int scale) : shared(sh), | |
| C(contexts), contextInfoList(contexts), hashTable(size >> 6), | |
| runMap(sh, contexts, (1 << 12), 127, StateMapType::Run), /* StateMap : s, n, lim, init */ // 63-255 | |
| stateMap(sh, contexts, (1 << 8), 511, StateMapType::BitHistory), /* StateMap : s, n, lim, init */ // 511-1023 | |
| bhMap8B(sh, contexts, (1 << 8), 511, StateMapType::Generic), /* StateMap : s, n, lim, init */ // 511-1023 | |
| bhMap12B(sh, contexts, (1 << 12), 511, StateMapType::Generic), /* StateMap : s, n, lim, init */ // 255-1023 | |
| index(0), mask(uint32_t(hashTable.size() - 1)), hashBits(ilog2(mask + 1)), scale(scale), contextflagsAll(0) { | |
| assert(size >= 64 && isPowerOf2(size)); | |
| static_assert(sizeof(Bucket16 <HashElementForContextMap, 7>) == (sizeof(HashElementForContextMap) + 2) * 7, "Something is wrong, please pack that struct"); | |
| } | |
| void ContextMap2::updatePendingContextsInSlot(HashElementForContextMap* const p, uint32_t c) { | |
| // in case of a collision updating (mixing) is slightly better (but slightly slower) then resetting, so we update | |
| StateTable::update(&p->slot1and2.bitState, (c >> 2) & 1, rnd); | |
| StateTable::update(&p->slot1and2.bitState0 + ((c >> 2) & 1), (c >> 1) & 1, rnd); | |
| StateTable::update(&p->slot1and2.bitState00 + ((c >> 1) & 3), (c & 1), rnd); | |
| } | |
| void ContextMap2::updatePendingContexts(uint32_t ctx, uint16_t checksum, uint32_t c) { | |
| // update pending bit histories for bits 2, 3, 4 (in slot 1) | |
| HashElementForContextMap* const p1A = hashTable[(ctx + (c >> 6)) & mask].find(checksum, &rnd); | |
| updatePendingContextsInSlot(p1A, c >> 3); | |
| // update pending bit histories for bits 5, 6, 7 (in slot 2) | |
| HashElementForContextMap* const p1B = hashTable[(ctx + (c >> 3)) & mask].find(checksum, &rnd); | |
| updatePendingContextsInSlot(p1B, c); | |
| } | |
| void ContextMap2::set(uint8_t ctxflags, const uint64_t contexthash) { | |
| assert(index >= 0 && index < C); | |
| ContextInfo *contextInfo = &contextInfoList[index]; | |
| const uint32_t ctx = contextInfo->tableIndex = finalize64(contexthash, hashBits); | |
| const uint16_t chk = contextInfo->tableChecksum = checksum16(contexthash, hashBits); | |
| HashElementForContextMap* const hashElementInSlot0 = hashTable[ctx].find(chk, &rnd); | |
| contextInfo->hashElementInSlot0 = hashElementInSlot0; | |
| contextInfo->hashElementInSlot012 = hashElementInSlot0; | |
| if (hashElementInSlot0->slot0.bitState <= 6) { // while constructing statistics for the first 3 bytes (states: 0; 1-2; 3-6) defer updating bit statistics in slot1 and slot2 | |
| ctxflags |= CM_DEFERRED; | |
| } | |
| else if (hashElementInSlot0->slot0.bitState <= 14) { // the first 3 bytes in this context are now known, it's time to update pending bit histories | |
| if (hashElementInSlot0->slot0.runcount == 2) { | |
| updatePendingContexts(ctx, chk, hashElementInSlot0->slot0.byte2 + 256); | |
| updatePendingContexts(ctx, chk, hashElementInSlot0->slot0.byte1 + 256); | |
| updatePendingContexts(ctx, chk, hashElementInSlot0->slot0.byte1 + 256); | |
| } | |
| else { | |
| updatePendingContexts(ctx, chk, hashElementInSlot0->slot0.byte3 + 256); | |
| updatePendingContexts(ctx, chk, hashElementInSlot0->slot0.byte2 + 256); | |
| updatePendingContexts(ctx, chk, hashElementInSlot0->slot0.byte1 + 256); | |
| } | |
| } | |
| contextInfo->flags = ctxflags; | |
| contextflagsAll |= ctxflags; | |
| index++; | |
| } | |
| void ContextMap2::skip(const uint8_t ctxflags) { | |
| assert(index >= 0 && index < C); | |
| contextInfoList[index].flags = ctxflags | CM_SKIPPED_CONTEXT; | |
| contextflagsAll |= ctxflags; | |
| index++; | |
| } | |
| void ContextMap2::skipn(const uint8_t ctxflags, const int n) { | |
| for (int i = 0; i < n; i++) | |
| skip(ctxflags); | |
| } | |
| ALWAYS_INLINE | |
| size_t ContextMap2::getStateByteLocation(const uint32_t bpos, const uint32_t c0) { | |
| uint32_t pis = 0; //state byte position in slot | |
| if (false) { | |
| // this version is for readability | |
| switch (bpos) { | |
| case 0: //slot0 | |
| pis = 0; | |
| break; | |
| case 1: //slot0 | |
| pis = 1 + (c0 & 1); | |
| break; | |
| case 2: //slot1 | |
| pis = 0; | |
| break; | |
| case 3: //slot1 | |
| pis = 1 + (c0 & 1); | |
| break; | |
| case 4: //slot1 | |
| pis = 3 + (c0 & 3); | |
| break; | |
| case 5: //slot2 | |
| break; | |
| case 6: //slot2 | |
| pis = 1 + (c0 & 1); | |
| break; | |
| case 7: //slot2 | |
| pis = 3 + (c0 & 3); | |
| break; | |
| } | |
| } | |
| else { | |
| // this is a speed optimized (branchless) version of the above | |
| const uint32_t smask = (UINT32_C(0x31031010) >> (bpos << 2)) & 0x0F; | |
| pis = smask + (c0 & smask); | |
| } | |
| return pis; | |
| } | |
| void ContextMap2::update() { | |
| INJECT_SHARED_y | |
| INJECT_SHARED_bpos | |
| INJECT_SHARED_c1 | |
| INJECT_SHARED_c0 | |
| for( uint32_t i = 0; i < index; i++ ) { | |
| ContextInfo* contextInfo = &contextInfoList[i]; | |
| if((contextInfo->flags & CM_SKIPPED_CONTEXT) == 0) { | |
| size_t stateIndex = getStateByteLocation((bpos - 1) & 7, (bpos == 0 ? c1 + 256u : c0) >> 1); | |
| uint8_t* pState = &contextInfo->hashElementInSlot012->states[stateIndex]; | |
| StateTable::update(pState, y, rnd); | |
| const auto slot0 = &contextInfo->hashElementInSlot0->slot0; | |
| ASSUME(bpos >= 0); | |
| ASSUME(bpos <= 7); | |
| if (bpos == 0) { | |
| // update byte history and run statistics | |
| if (slot0->bitState < 3) { | |
| slot0->byte3 = slot0->byte2 = slot0->byte1 = c1; | |
| slot0->runcount = 1; | |
| } | |
| else { | |
| const bool isMatch = slot0->byte1 == c1; | |
| if (isMatch) { | |
| uint8_t runCount = slot0->runcount; | |
| if (runCount < 255) { | |
| slot0->runcount = runCount + 1; | |
| } | |
| } | |
| else { | |
| // shift byte candidates | |
| slot0->runcount = 1; | |
| slot0->byte3 = slot0->byte2; | |
| slot0->byte2 = slot0->byte1; | |
| slot0->byte1 = c1; //last byte seen | |
| } | |
| } | |
| } | |
| else if( bpos==2 || bpos==5 ) { | |
| if (contextInfo->flags & CM_DEFERRED) { //when in deferred mode... | |
| // ...reconstruct bit states in temporary location from last seen bytes | |
| memset(&contextInfo->bitStateTmp, 0, 7); | |
| contextInfo->hashElementInSlot012 = &contextInfo->bitStateTmp; | |
| const uint8_t bit0state = slot0->bitState; | |
| if (bit0state >= 3) { // at least 1 byte was seen | |
| const uint8_t byte2 = bit0state >= 7 ? slot0->byte2 : slot0->byte1; | |
| const uint8_t mask = ((1 << bpos) - 1); | |
| const int shift = 8 - bpos; | |
| if (((c0 ^ (byte2 >> shift)) & mask) == 0) { // last 2/5 bits must match otherwise it's not the current slot location | |
| updatePendingContextsInSlot(&contextInfo->bitStateTmp, byte2 >> (shift - 3)); // simulate the current states at the temporary location | |
| } | |
| if (bit0state >= 7) { // at least 2 bytes were seen | |
| const uint8_t byte1 = slot0->byte1; | |
| if (((c0 ^ (byte1 >> shift)) & mask) == 0) { // last 2/5 bits must match otherwise it's not the current slot location | |
| updatePendingContextsInSlot(&contextInfo->bitStateTmp, byte1 >> (shift - 3)); // simulate the current states at the temporary location | |
| } | |
| } | |
| } | |
| } | |
| else { | |
| //when pbos==2: switch from slot 0 to slot 1 | |
| //when bpos==5: switch from slot 1 to slot 2 | |
| const uint32_t ctx = contextInfo->tableIndex; | |
| const uint16_t chk = contextInfo->tableChecksum; | |
| contextInfo->hashElementInSlot012 = hashTable[(ctx + c0) & mask].find(chk, &rnd); | |
| } | |
| } | |
| } | |
| } | |
| if( bpos == 0 ) { | |
| index = 0; | |
| contextflagsAll = 0; | |
| } // start over | |
| } | |
| void ContextMap2::setScale(const int Scale) { scale = Scale; } | |
| void ContextMap2::mix(Mixer &m) { | |
| shared->GetUpdateBroadcaster()->subscribe(this); | |
| stateMap.subscribe(); | |
| if ((contextflagsAll & CM_USE_RUN_STATS) != 0) | |
| runMap.subscribe(); | |
| if ((contextflagsAll & CM_USE_BYTE_HISTORY) != 0) { | |
| bhMap8B.subscribe(); | |
| bhMap12B.subscribe(); | |
| } | |
| order = 0; | |
| INJECT_SHARED_bpos | |
| INJECT_SHARED_c0 | |
| for( uint32_t i = 0; i < index; i++ ) { | |
| ContextInfo* contextInfo = &contextInfoList[i]; | |
| if((contextInfo->flags & CM_SKIPPED_CONTEXT) == 0 ) { | |
| const auto slot0 = &contextInfo->hashElementInSlot0->slot0; | |
| uint8_t* pState = &contextInfo->hashElementInSlot012->states[0] + getStateByteLocation(bpos, c0); | |
| const int state = *pState; | |
| const int n0 = StateTable::getNextState(state, 2); | |
| const int n1 = StateTable::getNextState(state, 3); | |
| const int bitIsUncertain = int(n0 != 0 && n1 != 0); | |
| // predict from last byte(s) in context | |
| uint8_t byteState = slot0->bitState; | |
| const bool complete1 = (byteState >= 3) || (byteState >= 1 && bpos == 0); | |
| const bool complete2 = (byteState >= 7) || (byteState >= 3 && bpos == 0); | |
| const bool complete3 = (byteState >= 15) || (byteState >= 7 && bpos == 0); | |
| if((contextInfo->flags & CM_USE_RUN_STATS) != 0 ) { | |
| const int bp = (UINT32_C(0x33322210) >> (bpos << 2)) & 0xF; // {bpos:0}->0 {bpos:1}->1 {bpos:2,3,4}->2 {bpos:5,6,7}->3 | |
| bool skipRunMap = true; | |
| if( complete1 ) { | |
| if(((slot0->byte1 + 256u) >> (8 - bpos)) == c0 ) { // 1st candidate (last byte seen) matches | |
| const int predictedBit = (slot0->byte1 >> (7 - bpos)) & 1; | |
| const int byte1IsUncertain = static_cast<const int>(slot0->byte2 != slot0->byte1); | |
| const int runCount = slot0->runcount; // 1..255 | |
| m.add(stretch(runMap.p2(i, runCount << 4 | bp << 2 | byte1IsUncertain << 1 | predictedBit)) >> (1 + byte1IsUncertain)); | |
| skipRunMap = false; | |
| } else if( complete2 && ((slot0->byte2 + 256u) >> (8 - bpos)) == c0 ) { // 2nd candidate matches | |
| const int predictedBit = (slot0->byte2 >> (7 - bpos)) & 1; | |
| const int byte2IsUncertain = static_cast<const int>(slot0->byte3 != slot0->byte2); | |
| m.add(stretch(runMap.p2(i, bitIsUncertain << 1 | predictedBit)) >> (2 + byte2IsUncertain)); | |
| skipRunMap = false; | |
| } | |
| // remark: considering the 3rd byte is not beneficial in most cases, except for some 8bpp images | |
| } | |
| if( skipRunMap ) { | |
| runMap.skip(i); | |
| m.add(0); | |
| } | |
| } | |
| else if ((contextflagsAll & CM_USE_RUN_STATS) != 0) { | |
| runMap.skip(i); | |
| } | |
| // predict from bit context | |
| if( state == 0 ) { | |
| stateMap.skip(i); | |
| m.add(0); | |
| m.add(0); | |
| m.add(0); | |
| m.add(0); | |
| } else { | |
| const int p1 = stateMap.p2(i, state); | |
| const int st = (stretch(p1) * scale) >> 8; | |
| const int contextIsYoung = int(state <= 2); | |
| m.add(st >> contextIsYoung); | |
| m.add(((p1 - 2048) * scale) >> 9); | |
| m.add((bitIsUncertain - 1) & st); // when both counts are nonzero add(0) otherwise add(st) | |
| const int p0 = 4095 - p1; | |
| m.add((((p1 & (-!n0)) - (p0 & (-!n1))) * scale) >> 10); | |
| order++; | |
| } | |
| if((contextInfo->flags & CM_USE_BYTE_HISTORY) != 0 ) { | |
| const int bhBits = | |
| (((slot0->byte1 >> (7 - bpos)) & 1)) | | |
| (((slot0->byte2 >> (7 - bpos)) & 1) << 1) | | |
| (((slot0->byte3 >> (7 - bpos)) & 1) << 2); | |
| int bhState = 0; // 4 bits | |
| if( complete3 ) { | |
| bhState = 8 | (bhBits); //we have seen 3 bytes (at least) | |
| } else if( complete2 ) { | |
| bhState = 4 | (bhBits & 3); //we have seen 2 bytes | |
| } else if( complete1 ) { | |
| bhState = 2 | (bhBits & 1); //we have seen 1 byte only | |
| } | |
| //else new context (bhState=0) | |
| const uint8_t stateGroup = StateTable::group(state); //0..31 | |
| m.add(stretch(bhMap8B.p2(i, bitIsUncertain << 7 | (bhState << 3) | bpos)) >> 2); // using bitIsUncertain is generally beneficial except for some 8bpp image (noticeable loss) | |
| m.add(stretch(bhMap12B.p2(i, stateGroup << 7 | (bhState << 3) | bpos)) >> 2); | |
| } | |
| else if ((contextflagsAll & CM_USE_BYTE_HISTORY) != 0) { | |
| bhMap8B.skip(i); | |
| bhMap12B.skip(i); | |
| } | |
| } else { //skipped context | |
| stateMap.skip(i); | |
| m.add(0); | |
| m.add(0); | |
| m.add(0); | |
| m.add(0); | |
| if((contextflagsAll & CM_USE_RUN_STATS) != 0) { | |
| runMap.skip(i); | |
| if((contextInfo->flags & CM_USE_RUN_STATS) != 0) | |
| m.add(0); | |
| } | |
| if((contextflagsAll & CM_USE_BYTE_HISTORY) != 0 ) { | |
| bhMap8B.skip(i); | |
| bhMap12B.skip(i); | |
| if ((contextInfo->flags & CM_USE_BYTE_HISTORY) != 0) { | |
| m.add(0); | |
| m.add(0); | |
| } | |
| } | |
| } | |
| } | |
| } | |
Xet Storage Details
- Size:
- 12.7 kB
- Xet hash:
- e3a325fb0065ce892c53f5c70150a2a4eb7e916ea54e4deb35abed494cb62cf3
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.