tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

compound_dictionary.c (6651B)


      1 /* Copyright 2017 Google Inc. All Rights Reserved.
      2 
      3   Distributed under MIT license.
      4   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
      5 */
      6 
      7 #include "compound_dictionary.h"
      8 
      9 #include "../common/platform.h"
     10 #include <brotli/shared_dictionary.h>
     11 #include "memory.h"
     12 
     13 static PreparedDictionary* CreatePreparedDictionaryWithParams(MemoryManager* m,
     14    const uint8_t* source, size_t source_size, uint32_t bucket_bits,
     15    uint32_t slot_bits, uint32_t hash_bits, uint16_t bucket_limit) {
     16  /* Step 1: create "bloated" hasher. */
     17  uint32_t num_slots = 1u << slot_bits;
     18  uint32_t num_buckets = 1u << bucket_bits;
     19  uint32_t hash_shift = 64u - bucket_bits;
     20  uint64_t hash_mask = (~((uint64_t)0U)) >> (64 - hash_bits);
     21  uint32_t slot_mask = num_slots - 1;
     22  size_t alloc_size = (sizeof(uint32_t) << slot_bits) +
     23      (sizeof(uint32_t) << slot_bits) +
     24      (sizeof(uint16_t) << bucket_bits) +
     25      (sizeof(uint32_t) << bucket_bits) +
     26      (sizeof(uint32_t) * source_size);
     27  uint8_t* flat = NULL;
     28  PreparedDictionary* result = NULL;
     29  uint16_t* num = NULL;
     30  uint32_t* bucket_heads = NULL;
     31  uint32_t* next_bucket = NULL;
     32  uint32_t* slot_offsets = NULL;
     33  uint16_t* heads = NULL;
     34  uint32_t* items = NULL;
     35  uint8_t** source_ref = NULL;
     36  uint32_t i;
     37  uint32_t* slot_size = NULL;
     38  uint32_t* slot_limit = NULL;
     39  uint32_t total_items = 0;
     40  if (slot_bits > 16) return NULL;
     41  if (slot_bits > bucket_bits) return NULL;
     42  if (bucket_bits - slot_bits >= 16) return NULL;
     43 
     44  flat = BROTLI_ALLOC(m, uint8_t, alloc_size);
     45  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(flat)) return NULL;
     46 
     47  slot_size = (uint32_t*)flat;
     48  slot_limit = (uint32_t*)(&slot_size[num_slots]);
     49  num = (uint16_t*)(&slot_limit[num_slots]);
     50  bucket_heads = (uint32_t*)(&num[num_buckets]);
     51  next_bucket = (uint32_t*)(&bucket_heads[num_buckets]);
     52  memset(num, 0, num_buckets * sizeof(num[0]));
     53 
     54  /* TODO(eustas): apply custom "store" order. */
     55  for (i = 0; i + 7 < source_size; ++i) {
     56    const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(&source[i]) & hash_mask) *
     57        kPreparedDictionaryHashMul64Long;
     58    const uint32_t key = (uint32_t)(h >> hash_shift);
     59    uint16_t count = num[key];
     60    next_bucket[i] = (count == 0) ? ((uint32_t)(-1)) : bucket_heads[key];
     61    bucket_heads[key] = i;
     62    count++;
     63    if (count > bucket_limit) count = bucket_limit;
     64    num[key] = count;
     65  }
     66 
     67  /* Step 2: find slot limits. */
     68  for (i = 0; i < num_slots; ++i) {
     69    BROTLI_BOOL overflow = BROTLI_FALSE;
     70    slot_limit[i] = bucket_limit;
     71    while (BROTLI_TRUE) {
     72      uint32_t limit = slot_limit[i];
     73      size_t j;
     74      uint32_t count = 0;
     75      overflow = BROTLI_FALSE;
     76      for (j = i; j < num_buckets; j += num_slots) {
     77        uint32_t size = num[j];
     78        /* Last chain may span behind 64K limit; overflow happens only if
     79           we are about to use 0xFFFF+ as item offset. */
     80        if (count >= 0xFFFF) {
     81          overflow = BROTLI_TRUE;
     82          break;
     83        }
     84        if (size > limit) size = limit;
     85        count += size;
     86      }
     87      if (!overflow) {
     88        slot_size[i] = count;
     89        total_items += count;
     90        break;
     91      }
     92      slot_limit[i]--;
     93    }
     94  }
     95 
     96  /* Step 3: transfer data to "slim" hasher. */
     97  alloc_size = sizeof(PreparedDictionary) + (sizeof(uint32_t) << slot_bits) +
     98      (sizeof(uint16_t) << bucket_bits) + (sizeof(uint32_t) * total_items) +
     99      sizeof(uint8_t*);
    100 
    101  result = (PreparedDictionary*)BROTLI_ALLOC(m, uint8_t, alloc_size);
    102  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(result)) {
    103    BROTLI_FREE(m, flat);
    104    return NULL;
    105  }
    106  slot_offsets = (uint32_t*)(&result[1]);
    107  heads = (uint16_t*)(&slot_offsets[num_slots]);
    108  items = (uint32_t*)(&heads[num_buckets]);
    109  source_ref = (uint8_t**)(&items[total_items]);
    110 
    111  result->magic = kLeanPreparedDictionaryMagic;
    112  result->num_items = total_items;
    113  result->source_size = (uint32_t)source_size;
    114  result->hash_bits = hash_bits;
    115  result->bucket_bits = bucket_bits;
    116  result->slot_bits = slot_bits;
    117  BROTLI_UNALIGNED_STORE_PTR(source_ref, source);
    118 
    119  total_items = 0;
    120  for (i = 0; i < num_slots; ++i) {
    121    slot_offsets[i] = total_items;
    122    total_items += slot_size[i];
    123    slot_size[i] = 0;
    124  }
    125  for (i = 0; i < num_buckets; ++i) {
    126    uint32_t slot = i & slot_mask;
    127    uint32_t count = num[i];
    128    uint32_t pos;
    129    size_t j;
    130    size_t cursor = slot_size[slot];
    131    if (count > slot_limit[slot]) count = slot_limit[slot];
    132    if (count == 0) {
    133      heads[i] = 0xFFFF;
    134      continue;
    135    }
    136    heads[i] = (uint16_t)cursor;
    137    cursor += slot_offsets[slot];
    138    slot_size[slot] += count;
    139    pos = bucket_heads[i];
    140    for (j = 0; j < count; j++) {
    141      items[cursor++] = pos;
    142      pos = next_bucket[pos];
    143    }
    144    items[cursor - 1] |= 0x80000000;
    145  }
    146 
    147  BROTLI_FREE(m, flat);
    148  return result;
    149 }
    150 
    151 PreparedDictionary* CreatePreparedDictionary(MemoryManager* m,
    152    const uint8_t* source, size_t source_size) {
    153  uint32_t bucket_bits = 17;
    154  uint32_t slot_bits = 7;
    155  uint32_t hash_bits = 40;
    156  uint16_t bucket_limit = 32;
    157  size_t volume = 16u << bucket_bits;
    158  /* Tune parameters to fit dictionary size. */
    159  while (volume < source_size && bucket_bits < 22) {
    160    bucket_bits++;
    161    slot_bits++;
    162    volume <<= 1;
    163  }
    164  return CreatePreparedDictionaryWithParams(m,
    165      source, source_size, bucket_bits, slot_bits, hash_bits, bucket_limit);
    166 }
    167 
    168 void DestroyPreparedDictionary(MemoryManager* m,
    169    PreparedDictionary* dictionary) {
    170  if (!dictionary) return;
    171  BROTLI_FREE(m, dictionary);
    172 }
    173 
    174 BROTLI_BOOL AttachPreparedDictionary(
    175    CompoundDictionary* compound, const PreparedDictionary* dictionary) {
    176  size_t length = 0;
    177  size_t index = 0;
    178 
    179  if (compound->num_chunks == SHARED_BROTLI_MAX_COMPOUND_DICTS) {
    180    return BROTLI_FALSE;
    181  }
    182 
    183  if (!dictionary) return BROTLI_FALSE;
    184 
    185  length = dictionary->source_size;
    186  index = compound->num_chunks;
    187  compound->total_size += length;
    188  compound->chunks[index] = dictionary;
    189  compound->chunk_offsets[index + 1] = compound->total_size;
    190  {
    191    uint32_t* slot_offsets = (uint32_t*)(&dictionary[1]);
    192    uint16_t* heads = (uint16_t*)(&slot_offsets[(size_t)1u << dictionary->slot_bits]);
    193    uint32_t* items = (uint32_t*)(&heads[(size_t)1u << dictionary->bucket_bits]);
    194    const void* tail = (void*)&items[dictionary->num_items];
    195    if (dictionary->magic == kPreparedDictionaryMagic) {
    196      compound->chunk_source[index] = (const uint8_t*)tail;
    197    } else {
    198      /* dictionary->magic == kLeanPreparedDictionaryMagic */
    199      compound->chunk_source[index] =
    200          (const uint8_t*)BROTLI_UNALIGNED_LOAD_PTR((const uint8_t**)tail);
    201    }
    202  }
    203  compound->num_chunks++;
    204  return BROTLI_TRUE;
    205 }