polkadot-js/client

View on GitHub
packages/client-db/src/engines/FileStructDb/index.ts

Summary

Maintainability
A
3 hrs
Test Coverage
// Copyright 2017-2019 @polkadot/client-db authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.

import { BaseDb, ProgressCb } from '@polkadot/db/types';
import { KVInfo } from './types';

import codec from '@polkadot/trie-codec';
import { BITMAP } from '@polkadot/trie-codec/constants';
import { logger, compactToU8a, compactFromU8a } from '@polkadot/util';

import Cache from './Cache';
import Impl from './Impl';
import { KEY_DATA_SIZE, U32_SIZE } from './constants';
import { serializeKey } from './util';

const l = logger('db/struct');

const TRIE_BRANCH_KEYS = 16;
const TRIE_BRANCH_LEN = TRIE_BRANCH_KEYS + 1;
const TRIE_ENC_SIZE = U32_SIZE + 2;

export default class FileStructDb extends Impl implements BaseDb {
  private _cache: Cache<string> = new Cache(16 * 1024);

  public drop (): void {
    l.error('drop() is not implemented');
  }

  public empty (): void {
    l.error('empty() is not implemented');
  }

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  public rename (base: string, file: string): void {
    l.error('rename() is not implemented');
  }

  public size (): number {
    l.error('size() is not implemented');

    return 0;
  }

  public maintain (fn: ProgressCb): void {
    fn({
      isCompleted: true,
      keys: 0,
      percent: 100
    });
  }

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  public del (key: Uint8Array): void {
    l.error('del() is not implemented');
  }

  private _get (key: Uint8Array): Uint8Array | null {
    const info = this._findValue(serializeKey(key));

    // console.log('_get', this._isTrie, key, info);

    if (!info || !info.valData) {
      return null;
    } else if (!this._isTrie) {
      return info.valData;
    } else if (!info.valData[0]) {
      return info.valData.subarray(1);
    }

    // 17 entries, assuming there may be an encoded value in there
    const recoded: (Uint8Array | null)[] = [
      null, null, null, null,
      null, null, null, null,
      null, null, null, null,
      null, null, null, null,
      null
    ];
    let offset = 3;
    const bitmap = info.valData[1] + (info.valData[2] << 8);

    for (let index = 0; index < TRIE_BRANCH_KEYS; index++) {
      if (bitmap & BITMAP[index]) {
        const fileAt = info.valData[offset++];
        const [length, keyAt] = compactFromU8a(info.valData.subarray(offset));

        recoded[index] = this._readKey(fileAt, keyAt.toNumber()).subarray(0, KEY_DATA_SIZE);
        offset += length;
      }
    }

    return codec.encode(recoded);
  }

  public get (key: Uint8Array): Uint8Array | null {
    // l.debug(() => ['get', { key }]);

    const keyStr = key.toString();
    const cached = this._cache.get(keyStr);

    if (cached) {
      return cached;
    }

    const value = this._get(key);

    if (value) {
      this._cache.set(keyStr, value);
    }

    return value;
  }

  public put (key: Uint8Array, value: Uint8Array): void {
    // l.debug(() => ['put', { key, value }]);

    this._cache.set(key.toString(), value);

    if (!this._isTrie) {
      this._findValue(serializeKey(key), value);
      return;
    }

    let adjusted: Uint8Array;
    const decoded = codec.decode(value);
    const isEncodable = Array.isArray(decoded) &&
      decoded.length === TRIE_BRANCH_LEN &&
      !decoded.some((value): boolean => !!value && value.length !== KEY_DATA_SIZE);

    // extension nodes are going away anyway, so just ignore, no worse off.
    if (isEncodable) {
      const recoded = new Uint8Array((TRIE_BRANCH_KEYS * TRIE_ENC_SIZE) + 1 + 2);
      let length = 3;
      let bitmap = 0;

      for (let index = 0; index < TRIE_BRANCH_KEYS; index++) {
        const u8a = (decoded as Uint8Array[])[index];

        if (u8a) {
          const key = serializeKey(u8a);
          const info = this._findValue(key, null, false);
          const atEnc = compactToU8a((info as KVInfo).keyAt);

          bitmap |= BITMAP[index];
          recoded[length++] = key.fileAt;

          recoded.set(atEnc, length);

          length += atEnc.length;
        }
      }

      recoded[0] = TRIE_BRANCH_KEYS;
      recoded[1] = bitmap & 0xff;
      recoded[2] = bitmap >> 8;
      adjusted = recoded.subarray(0, length);
    } else {
      adjusted = new Uint8Array(value.length + 1);
      adjusted.set(value, 1);
    }

    this._findValue(serializeKey(key), adjusted);
  }
}