polkadot-js/client

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

Summary

Maintainability
A
1 hr
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 { DiskDbOptions } from '../../types';

import fs from 'fs';
import mkdirp from 'mkdirp';
import path from 'path';

import Cache from './Cache';
import { DB_VERSION, DB_MAX_FILES, HDR_TOTAL_SIZE, KEY_TOTAL_SIZE } from './constants';

interface Fd {
  cache: Cache<number>;
  fd: number;
  file: string;
  size: number;
}

interface Fds {
  idx: Fd;
  key: Fd;
  val: Fd;
}

const CACHE_SIZES = {
  idx: 6 * 512,
  key: 4 * 512,
  val: 2 * 512
};

export default class Files {
  protected _isTrie = false;

  protected _isOpen = false;

  private _fds: Fds[] = [];

  private _path: string;

  public constructor (base: string, file: string, options: DiskDbOptions) {
    this._isTrie = options.isTrie;
    this._path = path.join(base, DB_VERSION, file);

    mkdirp.sync(this._path);
  }

  public close (): void {
    this._fds.forEach((fds): void =>
      Object.values(fds).forEach(({ fd }): void =>
        fs.closeSync(fd)
      )
    );

    this._isOpen = false;
  }

  public open (): void {
    for (let i = 0; i < DB_MAX_FILES; i++) {
      this._fds[i] = (['idx', 'key', 'val'] as (keyof Fds)[]).reduce((fds: any, type): Fds => {
        const count = `0${i.toString(16)}`.slice(-2);
        const file = path.join(this._path, `${count}.${type as string}`);

        if (!fs.existsSync(file)) {
          fs.writeFileSync(file, new Uint8Array(type === 'idx' ? HDR_TOTAL_SIZE : 0));
        }

        const cache = new Cache<number>(CACHE_SIZES[type]);
        const fd = fs.openSync(file, 'r+');
        const size = fs.fstatSync(fd).size;

        fds[type] = { cache, fd, file, size };

        return fds;
      }, {});
    }
  }

  private __append (type: keyof Fds, fileAt: number, buffer: Uint8Array): number {
    const fd = this._fds[fileAt][type];
    const at = fd.size;

    fs.writeSync(fd.fd, buffer, 0, buffer.length, at);
    fd.size += buffer.length;
    fd.cache.set(at, buffer);

    return at;
  }

  private __read (type: keyof Fds, fileAt: number, at: number, length: number): Uint8Array {
    const fd = this._fds[fileAt][type];
    const cached = fd.cache.get(at);

    if (cached) {
      return cached;
    }

    const buffer = new Uint8Array(length);

    fs.readSync(fd.fd, buffer, 0, length, at);
    fd.cache.set(at, buffer);

    return buffer;
  }

  private __update (type: keyof Fds, fileAt: number, at: number, buffer: Uint8Array): number {
    const fd = this._fds[fileAt][type];

    fs.writeSync(fd.fd, buffer, 0, buffer.length, at);
    fd.cache.set(at, buffer);

    return at;
  }

  protected _appendHdr (fileAt: number, buffer: Uint8Array): number {
    return this.__append('idx', fileAt, buffer) / HDR_TOTAL_SIZE;
  }

  protected _appendKey (fileAt: number, buffer: Uint8Array): number {
    return this.__append('key', fileAt, buffer) / KEY_TOTAL_SIZE;
  }

  protected _appendVal (fileAt: number, buffer: Uint8Array): number {
    return this.__append('val', fileAt, buffer);
  }

  protected _readHdr (fileAt: number, at: number): Uint8Array {
    return this.__read('idx', fileAt, at * HDR_TOTAL_SIZE, HDR_TOTAL_SIZE);
  }

  protected _readKey (fileAt: number, at: number): Uint8Array {
    return this.__read('key', fileAt, at * KEY_TOTAL_SIZE, KEY_TOTAL_SIZE);
  }

  protected _readVal (fileAt: number, at: number, length: number): Uint8Array {
    return this.__read('val', fileAt, at, length);
  }

  protected _updateHdr (fileAt: number, at: number, buffer: Uint8Array): void {
    this.__update('idx', fileAt, at * HDR_TOTAL_SIZE, buffer);
  }

  protected _updateKey (fileAt: number, at: number, buffer: Uint8Array): void {
    this.__update('key', fileAt, at * KEY_TOTAL_SIZE, buffer);
  }
}