Edit

kc3-lang/brotli/c/enc/compound_dictionary.c

Branch :

  • Show log

    Commit

  • Author : Eugene Kliuchnikov
    Date : 2023-01-04 12:10:29
    Hash : 0ea46038
    Message : Fix MSVC warning (#998) Fix #875

  • c/enc/compound_dictionary.c
  • /* Copyright 2017 Google Inc. All Rights Reserved.
    
       Distributed under MIT license.
       See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
    */
    
    #include "compound_dictionary.h"
    
    #include <brotli/types.h>
    
    #include "../common/platform.h"
    #include "memory.h"
    #include "quality.h"
    
    static PreparedDictionary* CreatePreparedDictionaryWithParams(MemoryManager* m,
        const uint8_t* source, size_t source_size, uint32_t bucket_bits,
        uint32_t slot_bits, uint32_t hash_bits, uint16_t bucket_limit) {
      /* Step 1: create "bloated" hasher. */
      uint32_t num_slots = 1u << slot_bits;
      uint32_t num_buckets = 1u << bucket_bits;
      uint32_t hash_shift = 64u - bucket_bits;
      uint64_t hash_mask = (~((uint64_t)0U)) >> (64 - hash_bits);
      uint32_t slot_mask = num_slots - 1;
      size_t alloc_size = (sizeof(uint32_t) << slot_bits) +
          (sizeof(uint32_t) << slot_bits) +
          (sizeof(uint16_t) << bucket_bits) +
          (sizeof(uint32_t) << bucket_bits) +
          (sizeof(uint32_t) * source_size);
      uint8_t* flat = NULL;
      PreparedDictionary* result = NULL;
      uint16_t* num = NULL;
      uint32_t* bucket_heads = NULL;
      uint32_t* next_bucket = NULL;
      uint32_t* slot_offsets = NULL;
      uint16_t* heads = NULL;
      uint32_t* items = NULL;
      uint8_t** source_ref = NULL;
      uint32_t i;
      uint32_t* slot_size = NULL;
      uint32_t* slot_limit = NULL;
      uint32_t total_items = 0;
      if (slot_bits > 16) return NULL;
      if (slot_bits > bucket_bits) return NULL;
      if (bucket_bits - slot_bits >= 16) return NULL;
    
      flat = BROTLI_ALLOC(m, uint8_t, alloc_size);
      if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(flat)) return NULL;
    
      slot_size = (uint32_t*)flat;
      slot_limit = (uint32_t*)(&slot_size[num_slots]);
      num = (uint16_t*)(&slot_limit[num_slots]);
      bucket_heads = (uint32_t*)(&num[num_buckets]);
      next_bucket = (uint32_t*)(&bucket_heads[num_buckets]);
      memset(num, 0, num_buckets * sizeof(num[0]));
    
      /* TODO(eustas): apply custom "store" order. */
      for (i = 0; i + 7 < source_size; ++i) {
        const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(&source[i]) & hash_mask) *
            kPreparedDictionaryHashMul64Long;
        const uint32_t key = (uint32_t)(h >> hash_shift);
        uint16_t count = num[key];
        next_bucket[i] = (count == 0) ? ((uint32_t)(-1)) : bucket_heads[key];
        bucket_heads[key] = i;
        count++;
        if (count > bucket_limit) count = bucket_limit;
        num[key] = count;
      }
    
      /* Step 2: find slot limits. */
      for (i = 0; i < num_slots; ++i) {
        BROTLI_BOOL overflow = BROTLI_FALSE;
        slot_limit[i] = bucket_limit;
        while (BROTLI_TRUE) {
          uint32_t limit = slot_limit[i];
          size_t j;
          uint32_t count = 0;
          overflow = BROTLI_FALSE;
          for (j = i; j < num_buckets; j += num_slots) {
            uint32_t size = num[j];
            /* Last chain may span behind 64K limit; overflow happens only if
               we are about to use 0xFFFF+ as item offset. */
            if (count >= 0xFFFF) {
              overflow = BROTLI_TRUE;
              break;
            }
            if (size > limit) size = limit;
            count += size;
          }
          if (!overflow) {
            slot_size[i] = count;
            total_items += count;
            break;
          }
          slot_limit[i]--;
        }
      }
    
      /* Step 3: transfer data to "slim" hasher. */
      alloc_size = sizeof(PreparedDictionary) + (sizeof(uint32_t) << slot_bits) +
          (sizeof(uint16_t) << bucket_bits) + (sizeof(uint32_t) * total_items) +
          sizeof(uint8_t*);
    
      result = (PreparedDictionary*)BROTLI_ALLOC(m, uint8_t, alloc_size);
      if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(result)) {
        BROTLI_FREE(m, flat);
        return NULL;
      }
      slot_offsets = (uint32_t*)(&result[1]);
      heads = (uint16_t*)(&slot_offsets[num_slots]);
      items = (uint32_t*)(&heads[num_buckets]);
      source_ref = (uint8_t**)(&items[total_items]);
    
      result->magic = kLeanPreparedDictionaryMagic;
      result->num_items = total_items;
      result->source_size = (uint32_t)source_size;
      result->hash_bits = hash_bits;
      result->bucket_bits = bucket_bits;
      result->slot_bits = slot_bits;
      BROTLI_UNALIGNED_STORE_PTR(source_ref, source);
    
      total_items = 0;
      for (i = 0; i < num_slots; ++i) {
        slot_offsets[i] = total_items;
        total_items += slot_size[i];
        slot_size[i] = 0;
      }
      for (i = 0; i < num_buckets; ++i) {
        uint32_t slot = i & slot_mask;
        uint32_t count = num[i];
        uint32_t pos;
        size_t j;
        size_t cursor = slot_size[slot];
        if (count > slot_limit[slot]) count = slot_limit[slot];
        if (count == 0) {
          heads[i] = 0xFFFF;
          continue;
        }
        heads[i] = (uint16_t)cursor;
        cursor += slot_offsets[slot];
        slot_size[slot] += count;
        pos = bucket_heads[i];
        for (j = 0; j < count; j++) {
          items[cursor++] = pos;
          pos = next_bucket[pos];
        }
        items[cursor - 1] |= 0x80000000;
      }
    
      BROTLI_FREE(m, flat);
      return result;
    }
    
    PreparedDictionary* CreatePreparedDictionary(MemoryManager* m,
        const uint8_t* source, size_t source_size) {
      uint32_t bucket_bits = 17;
      uint32_t slot_bits = 7;
      uint32_t hash_bits = 40;
      uint16_t bucket_limit = 32;
      size_t volume = 16u << bucket_bits;
      /* Tune parameters to fit dictionary size. */
      while (volume < source_size && bucket_bits < 22) {
        bucket_bits++;
        slot_bits++;
        volume <<= 1;
      }
      return CreatePreparedDictionaryWithParams(m,
          source, source_size, bucket_bits, slot_bits, hash_bits, bucket_limit);
    }
    
    void DestroyPreparedDictionary(MemoryManager* m,
        PreparedDictionary* dictionary) {
      if (!dictionary) return;
      BROTLI_FREE(m, dictionary);
    }
    
    BROTLI_BOOL AttachPreparedDictionary(
        CompoundDictionary* compound, const PreparedDictionary* dictionary) {
      size_t length = 0;
      size_t index = 0;
    
      if (compound->num_chunks == SHARED_BROTLI_MAX_COMPOUND_DICTS) {
        return BROTLI_FALSE;
      }
    
      if (!dictionary) return BROTLI_FALSE;
    
      length = dictionary->source_size;
      index = compound->num_chunks;
      compound->total_size += length;
      compound->chunks[index] = dictionary;
      compound->chunk_offsets[index + 1] = compound->total_size;
      {
        uint32_t* slot_offsets = (uint32_t*)(&dictionary[1]);
        uint16_t* heads = (uint16_t*)(&slot_offsets[1u << dictionary->slot_bits]);
        uint32_t* items = (uint32_t*)(&heads[1u << dictionary->bucket_bits]);
        const void* tail = (void*)&items[dictionary->num_items];
        if (dictionary->magic == kPreparedDictionaryMagic) {
          compound->chunk_source[index] = (const uint8_t*)tail;
        } else {
          /* dictionary->magic == kLeanPreparedDictionaryMagic */
          compound->chunk_source[index] =
              (const uint8_t*)BROTLI_UNALIGNED_LOAD_PTR((const uint8_t**)tail);
        }
      }
      compound->num_chunks++;
      return BROTLI_TRUE;
    }