Add ListBuilder (#1669)

A ListBuilder allows creating large lists efficiently
This commit is contained in:
Erik Arvidsson
2016-05-27 14:49:22 -07:00
parent db9bb15786
commit dbd4c572df
5 changed files with 99 additions and 6 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@attic/noms",
"version": "41.4.0",
"version": "41.5.0",
"description": "Noms JS SDK",
"repository": "https://github.com/attic-labs/noms",
"main": "dist/commonjs/noms.js",

View File

@@ -26,7 +26,7 @@ import {
} from './test-util.js';
import {MetaTuple, newListMetaSequence} from './meta-sequence.js';
import {invariant, notNull} from './assert.js';
import List from './list.js';
import List, {ListWriter} from './list.js';
import {equals} from './compare.js';
const testListSize = 5000;
@@ -492,4 +492,55 @@ suite('Diff List', () => {
assert.strictEqual('Load limit exceeded', exMessage);
});
test('ListWriter', async () => {
const values = intSequence(15);
const l = new List(values);
const w = new ListWriter();
for (let i = 0; i < values.length; i++) {
w.write(values[i]);
}
w.close();
const l2 = w.list;
const l3 = w.list;
assert.isTrue(equals(l, l2));
assert.strictEqual(l2, l3);
});
test('ListWriter close throws', () => {
const values = intSequence(15);
const w = new ListWriter();
for (let i = 0; i < values.length; i++) {
w.write(values[i]);
}
w.close();
let ex;
try {
w.close(); // Cannot close twice.
} catch (e) {
ex = e;
}
assert.instanceOf(ex, TypeError);
});
test('ListWriter write throws', () => {
const values = intSequence(15);
const w = new ListWriter();
for (let i = 0; i < values.length; i++) {
w.write(values[i]);
}
w.close();
let ex;
try {
w.write(42); // Cannot write after close.
} catch (e) {
ex = e;
}
assert.instanceOf(ex, TypeError);
});
});

View File

@@ -12,14 +12,18 @@ import {IndexedSequence, IndexedSequenceIterator} from './indexed-sequence.js';
import {diff} from './indexed-sequence-diff.js';
import {getHashOfValue} from './get-hash.js';
import {invariant} from './assert.js';
import {MetaTuple, newIndexedMetaSequenceBoundaryChecker,
newIndexedMetaSequenceChunkFn} from './meta-sequence.js';
import {
MetaTuple,
newIndexedMetaSequenceBoundaryChecker,
newIndexedMetaSequenceChunkFn,
} from './meta-sequence.js';
import {sha1Size} from './hash.js';
import Ref from './ref.js';
import {getValueChunks} from './sequence.js';
import {makeListType, makeUnionType, getTypeOfValue} from './type.js';
import {equals} from './compare.js';
import {Kind} from './noms-kind.js';
import SequenceChunker from './sequence-chunker.js';
const listWindowSize = 64;
const listPattern = ((1 << 6) | 0) - 1;
@@ -154,3 +158,41 @@ function clampIndex(idx: number, length: number): number {
return idx < 0 ? Math.max(0, length + idx) : idx;
}
type ListWriterState = 'writable' | 'closed';
export class ListWriter<T: Value> {
_state: ListWriterState;
_list: ?List<T>;
_chunker: SequenceChunker<List<T>, T, ListLeafSequence<T>>;
constructor() {
this._state = 'writable';
this._chunker = new SequenceChunker(null, newListLeafChunkFn(),
newIndexedMetaSequenceChunkFn(Kind.List, null), newListLeafBoundaryChecker(),
newIndexedMetaSequenceBoundaryChecker);
}
write(item: T) {
assert(this._state === 'writable');
this._chunker.append(item);
}
close() {
assert(this._state === 'writable');
this._list = this._chunker.doneSync();
this._state = 'closed';
}
get list(): List {
assert(this._state === 'closed');
invariant(this._list);
return this._list;
}
}
function assert(v: any) {
if (!v) {
throw new TypeError('Invalid usage of ListWriter');
}
}

View File

@@ -23,7 +23,7 @@ export {
export {encodeNomsValue} from './encode.js';
export {invariant, notNull} from './assert.js';
export {isPrimitiveKind, Kind, kindToString} from './noms-kind.js';
export {default as List, ListLeafSequence} from './list.js';
export {default as List, ListWriter, ListLeafSequence} from './list.js';
export {default as Map, MapLeafSequence} from './map.js';
export {default as Set, SetLeafSequence} from './set.js';
export {IndexedSequence} from './indexed-sequence.js';

View File

@@ -60,7 +60,7 @@ export function chunkSequenceSync<C: Collection, S>(
return chunker.doneSync();
}
export default class SequenceChunker<C: Collection, S, U:Sequence> {
export default class SequenceChunker<C: Collection, S, U: Sequence> {
_cursor: ?SequenceCursor<S, U>;
_isOnChunkBoundary: boolean;
_parent: ?SequenceChunker<C, MetaTuple, MetaSequence>;