From 6ee3b37cd3ee898e8e581b4af71ee023b287e4e7 Mon Sep 17 00:00:00 2001 From: Rafael Weinstein Date: Thu, 17 Dec 2015 09:15:04 -0800 Subject: [PATCH] JS Collection refactor, splore, crunchbase ui, & pitchmap update --- clients/crunchbase/ui/package.json | 2 +- clients/crunchbase/ui/src/assert.js | 7 -- clients/crunchbase/ui/src/data.js | 107 +++++++++++-------------- clients/pitchmap/ui/heat_map.js | 50 ++++++------ clients/pitchmap/ui/main.js | 45 ++++++----- clients/splore/main.js | 116 +++++++++++++--------------- clients/tagshow/assert.js | 7 -- clients/tagshow/datasetpicker.js | 3 +- js/src/chunk_serializer.js | 2 +- js/src/collection.js | 17 ++++ js/src/decode.js | 72 ++++++++--------- js/src/decode_test.js | 87 ++++++++++++--------- js/src/encode.js | 35 +++++---- js/src/encode_test.js | 47 ++++++----- js/src/indexed_sequence.js | 5 +- js/src/list.js | 60 ++++---------- js/src/list_test.js | 26 +++---- js/src/map.js | 52 ++++--------- js/src/map_test.js | 50 ++++++------ js/src/meta_sequence.js | 74 +++++++++++++++--- js/src/noms.js | 11 ++- js/src/ordered_sequence.js | 12 +-- js/src/ref.js | 65 ++++++++-------- js/src/ref_test.js | 4 +- js/src/sequence.js | 20 ++--- js/src/set.js | 59 +++++--------- js/src/set_test.js | 50 +++++++----- js/src/struct.js | 4 +- js/src/value.js | 13 +++- 29 files changed, 545 insertions(+), 557 deletions(-) delete mode 100644 clients/crunchbase/ui/src/assert.js delete mode 100644 clients/tagshow/assert.js create mode 100644 js/src/collection.js diff --git a/clients/crunchbase/ui/package.json b/clients/crunchbase/ui/package.json index 9d60a0671c..09f47863f1 100644 --- a/clients/crunchbase/ui/package.json +++ b/clients/crunchbase/ui/package.json @@ -34,7 +34,7 @@ "watchify": "^3.6" }, "scripts": { - "start": "cp node_modules/nvd3/build/nv.d3.css nv.d3.css; NOMS_SERVER=http://localhost:8000 NOMS_DATASET_ID=crunchbase/index watchify -o out.js -v -d src/main.js", + "start": "cp node_modules/nvd3/build/nv.d3.css nv.d3.css; NOMS_SERVER=http://localhost:8000 NOMS_DATASET_ID=crunchbase/index watchify -o out.js -v -d node_modules/babel-regenerator-runtime/runtime.js src/main.js", "build": "./.npm_build_helper.sh", "test": "rm -f out.js && eslint src/ && flow src/" }, diff --git a/clients/crunchbase/ui/src/assert.js b/clients/crunchbase/ui/src/assert.js deleted file mode 100644 index 024e383892..0000000000 --- a/clients/crunchbase/ui/src/assert.js +++ /dev/null @@ -1,7 +0,0 @@ -// @flow - -export function invariant(exp: any, message: string = 'Invariant violated') { - if (!exp) { - throw new Error(message); - } -} diff --git a/clients/crunchbase/ui/src/data.js b/clients/crunchbase/ui/src/data.js index c24c19bd88..f0522b5adc 100644 --- a/clients/crunchbase/ui/src/data.js +++ b/clients/crunchbase/ui/src/data.js @@ -1,8 +1,8 @@ // @flow import {readValue, Struct, makeType, Ref, registerPackage} from 'noms'; -import {invariant} from './assert.js'; import type {ChunkStore, Package} from 'noms'; +import {invariant, Kind, NomsMap, NomsSet, SetLeafSequence, makeCompoundType, makePrimitiveType} from 'noms'; type RoundTypeEnum = 0 | 1 | 2; const Seed = 0; @@ -34,15 +34,14 @@ export default class DataManager { _datasetId: string; _keyClass: any; _quarterClass: any; - _index: Map; - _datasetP: ?Promise>; + _datasetP: ?Promise>; _packageP: ?Promise; - _categorySetP: ?Promise>; - _timeSetP: ?Promise>; - _seedSetP: ?Promise>; - _seriesASetP: ?Promise>; - _seriesBSetP: ?Promise>; + _categorySetP: ?Promise>; + _timeSetP: ?Promise>; + _seedSetP: ?Promise>; + _seriesASetP: ?Promise>; + _seriesBSetP: ?Promise>; _data: ?DataArray; _time: ?TimeOption; @@ -55,7 +54,6 @@ export default class DataManager { this._quarterClass = null; this._datasetP = null; this._packageP = null; - this._index = new Map(); this._timeSetP = null; this._categorySetP = null; @@ -68,7 +66,7 @@ export default class DataManager { this._category = ''; } - async _getDataset(): Promise> { + async _getDataset(): Promise> { if (this._datasetP) { return this._datasetP; } @@ -82,8 +80,6 @@ export default class DataManager { let ds = await this._getDataset(); this._packageP = getKeyPackage(ds, this._store); - this._index = convertMap(ds); - invariant(this._packageP); return this._packageP; } @@ -137,19 +133,23 @@ export default class DataManager { await Promise.all([this._seedSetP, this._seriesASetP, this._seriesBSetP, this._timeSetP, this._categorySetP]); - let baseSet = intersectRounds(timeSet, categorySet); + let baseSet = await timeSet.intersect(categorySet); + let sets = await Promise.all([baseSet.intersect(seedSet), + baseSet.intersect(seriesASet), baseSet.intersect(seriesBSet)]); + let ptiles = await Promise.all([percentiles(sets[0]), percentiles(sets[1]), + percentiles(sets[2])]); return this._data = [ { - values: percentiles(intersectRounds(baseSet, seedSet)), + values: ptiles[0], key: 'Seed' }, { - values: percentiles(intersectRounds(baseSet, seriesASet)), + values: ptiles[1], key: 'A' }, { - values: percentiles(intersectRounds(baseSet, seriesBSet)), + values: ptiles[2], key: 'B' } ]; @@ -167,31 +167,35 @@ export default class DataManager { return k.ref; } - async _getSetOfRounds(p: KeyParam): Promise> { - let s = (await this._getKeyRef(p)).toString(); - let v = this._index.get(s); - if (v === undefined) { - return new Set(); + async _getSetOfRounds(p: KeyParam): Promise> { + let r = await this._getKeyRef(p); + invariant(this._datasetP); + let map = await this._datasetP; + let setRef = await map.get(r); + if (setRef === undefined) { + // TODO: Cleanup the NomsSet api (it shouldn't be this hard to create an emptySet) + return new NomsSet(this._store, setTr, new SetLeafSequence(setTr, [])); } - let set = readValue(v, this._store); - return set || new Set(); - } + return readValue(setRef, this._store); + } } +// TODO: This is actually the wrong type. Fix when we have JS codegen. +const setTr = makeCompoundType(Kind.Set, makeCompoundType(Kind.Ref, makePrimitiveType(Kind.Value))); + /** * Loads the first key in the index and gets the package from the type. */ -async function getKeyPackage(index: Map, store: ChunkStore): +async function getKeyPackage(index: NomsMap, store: ChunkStore): Promise { - let ref; - for (let v of index.keys()) { - ref = v; - break; - } - invariant(ref instanceof Ref); - let key = await readValue(ref, store); - let pkg = await readValue(key.type.packageRef, store); + let kv = await index.first(); + invariant(kv); + let ref = kv[0]; + let key: Struct = await readValue(ref, store); + invariant(key); + let pkg: Package = await readValue(key.type.packageRef, store); + invariant(pkg); registerPackage(pkg); return pkg; } @@ -208,46 +212,27 @@ function getStructClass(pkg, name) { }; } -async function getDataset(id: string, httpStore: ChunkStore): any { +async function getDataset(id: string, httpStore: ChunkStore): Promise> { let rootRef = await httpStore.getRoot(); - let datasets = await readValue(rootRef, httpStore); - let commitRef = datasets.get(id); - let commit = await readValue(commitRef, httpStore); + let datasets: Map = await readValue(rootRef, httpStore); + let commitRef = await datasets.get(id); + invariant(commitRef); + let commit: Struct = await readValue(commitRef, httpStore); + invariant(commit); return commit.get('value'); } -function convertMap(map: Map): Map { - let m = new Map(); - map.forEach((v, k) => { - m.set(k.toString(), v); - }); - return m; -} -function intersectRounds(a: Set, b: Set): Set { - let sa = new Set(); - a.forEach(v => { - sa.add(v.ref.toString()); - }); - let s = new Set(); - b.forEach(v => { - if (sa.has(v.ref.toString())) { - s.add(v); - } - }); - return s; -} - -function percentiles(s: Set): Array<{x: number, y: number}> { +async function percentiles(s: NomsSet): Promise> { let arr: Array = []; - for (let round of s) { + await s.forEach(round => { let v = round.get('Raised'); if (v > 0) { arr.push(v); } + }); - } arr.sort((a, b) => a - b); let len = arr.length; if (len === 0) { diff --git a/clients/pitchmap/ui/heat_map.js b/clients/pitchmap/ui/heat_map.js index a92a52b529..4a74033e8f 100644 --- a/clients/pitchmap/ui/heat_map.js +++ b/clients/pitchmap/ui/heat_map.js @@ -1,7 +1,7 @@ // @flow import React from 'react'; -import {readValue, HttpStore, Ref, Struct} from 'noms'; +import {HttpStore, NomsList, notNull, readValue, Ref} from 'noms'; const IMAGE_WIDTH_PX = 286; const IMAGE_HEIGHT_PX = 324; @@ -17,13 +17,18 @@ function feetToPixels(f: number): number { } type Props = { - pitchListRef: Ref, + pitchListRefP: Promise, httpStore: HttpStore }; +type Point = { + x: number, + y: number +}; + type State = { loaded: boolean, - pitchList: ?Array + pointList: Array }; export default class HeatMap extends React.Component { @@ -32,7 +37,7 @@ export default class HeatMap extends React.Component { this.state = { loaded: false, - pitchList: null + pointList: [] }; } @@ -41,11 +46,20 @@ export default class HeatMap extends React.Component { return; } - let pitchList = await readValue(this.props.pitchListRef, this.props.httpStore); - if (Array.isArray(pitchList)) { + let pitchListRef = notNull(await this.props.pitchListRefP); + let pitchList = await readValue(pitchListRef, this.props.httpStore); + + if (pitchList instanceof NomsList) { + let pointList = []; + await pitchList.forEach(p => { + pointList.push({ + x: -1 + ORIGIN_X_PIXELS + feetToPixels(p.get('X')), + y: -1 + ORIGIN_Z_PIXELS - feetToPixels(p.get('Z')) + }); + }); this.setState({ loaded: true, - pitchList: pitchList + pointList: pointList }); } else { throw new Error('Unexpected type of pitchList'); @@ -77,27 +91,15 @@ export default class HeatMap extends React.Component { } getPoints(): Array { - if (!this.state.loaded) { - return []; - } - - if (!this.state.pitchList) { - throw new Error('pitchList not loaded'); - } - - return this.state.pitchList.map(p => { - let w = 2; - let h = 2; - let x = - w / 2 + ORIGIN_X_PIXELS + feetToPixels(p.get('X')); - let y = - h / 2 + ORIGIN_Z_PIXELS - feetToPixels(p.get('Z')); + return this.state.pointList.map(p => { return
{ httpStore = new HttpStore('http://localhost:8000'); let rootRef = await httpStore.getRoot(); - let datasets = await readValue(rootRef, httpStore); - let commitRef = datasets.get('mlb/heatmap'); - let commit = await readValue(commitRef, httpStore); + let datasets:NomsMap = await readValue(rootRef, httpStore); + let commitRef = await datasets.get('mlb/heatmap'); + invariant(commitRef); + let commit:Struct = await readValue(commitRef, httpStore); let pitchersMap = commit.get('value'); - renderPitchersMap(pitchersMap); + let pitchers = []; + await pitchersMap.forEach((ref, pitcher) => { + pitchers.push(pitcher); + }); + + pitchers.sort(); + renderPitchersMap(pitchersMap, pitchers); }); type Props = { - pitchersMap: Map + pitchersMap: NomsMap, + pitchers: Array }; type State = { - currentPitcher: string, - pitchers: Array + currentPitcher: string }; class PitcherList extends React.Component { constructor(props) { super(props); - let pitchers = []; - this.props.pitchersMap.forEach((ref, pitcher) => { - pitchers.push(pitcher); - }); - pitchers.sort(); - this.state = { - currentPitcher: pitchers[0], - pitchers: pitchers + currentPitcher: props.pitchers[0] }; } render() : React.Element { let currentPitcher = this.state.currentPitcher; - let pitchListRef = this.props.pitchersMap.get(currentPitcher); + let pitchListRefP = this.props.pitchersMap.get(currentPitcher); let onChangePitcher = e => { this.setState({ - currentPitcher: e.target.value, - pitchers: this.state.pitchers + currentPitcher: e.target.value }); }; return
- +
; } } -function renderPitchersMap(map: Map) { +function renderPitchersMap(map: NomsMap, pitchers: Array) { let renderNode = document.getElementById('heatmap'); - ReactDOM.render(, renderNode); + ReactDOM.render(, renderNode); } diff --git a/clients/splore/main.js b/clients/splore/main.js index c7c6e55db2..6def3dd2a1 100644 --- a/clients/splore/main.js +++ b/clients/splore/main.js @@ -1,10 +1,10 @@ // @flow -import {layout, NodeGraph, TreeNode} from './buchheim.js'; import Layout from './layout.js'; import React from 'react'; // eslint-disable-line no-unused-vars import ReactDOM from 'react-dom'; -import {CompoundList, CompoundMap, CompoundSet, ListLeaf, MapLeaf, readValue, SetLeaf, HttpStore, Ref, Struct} from 'noms'; +import {HttpStore, invariant, IndexedMetaSequence, ListLeafSequence, MapLeafSequence, OrderedMetaSequence, NomsList, NomsMap, NomsSet, readValue, Ref, SetLeafSequence, Struct} from 'noms'; +import {layout, NodeGraph, TreeNode} from './buchheim.js'; let data: NodeGraph = {nodes: {}, links: {}}; let rootRef: Ref; @@ -39,6 +39,20 @@ function formatKeyString(v: any): string { function handleChunkLoad(ref: Ref, val: any, fromRef: ?string) { let counter = 0; + function processMetaSequence(id, sequence: IndexedMetaSequence | OrderedMetaSequence, name: string) { + data.nodes[id] = {name: name}; + sequence.items.forEach(tuple => { + let kid = process(ref, formatKeyString(tuple.value), id); + if (kid) { + data.nodes[kid].isOpen = true; + + process(ref, tuple.ref, kid); + } else { + throw new Error('No kid id.'); + } + }); + } + function process(ref, val, fromId): ?string { if (typeof val === 'undefined') { return null; @@ -68,28 +82,45 @@ function handleChunkLoad(ref: Ref, val: any, fromRef: ?string) { if (val instanceof Blob) { data.nodes[id] = {name: `Blob (${val.size})`}; - } else if (val instanceof ListLeaf) { - data.nodes[id] = {name: `List (${val.length})`}; - val.items.forEach(c => process(ref, c, id)); - } else if (val instanceof SetLeaf) { - data.nodes[id] = {name: `Set (${val.size})`}; - val.items.forEach(c => process(ref, c, id)); - } else if (val instanceof MapLeaf) { - data.nodes[id] = {name: `Map (${val.size})`}; - val.items.forEach(entry => { - let k = entry.key; - let v = entry.value; - // TODO: handle non-string keys - let kid = process(ref, k, id); - if (kid) { - // Start map keys open, just makes it easier to use. - data.nodes[kid].isOpen = true; + } else if (val instanceof NomsList) { + let sequence = val.sequence; + if (sequence instanceof ListLeafSequence) { + data.nodes[id] = {name: `List (${val.length})`}; + sequence.items.forEach(c => process(ref, c, id)); + } else { + invariant(sequence instanceof IndexedMetaSequence); + processMetaSequence(id, sequence, 'ListNode'); + } + } else if (val instanceof NomsSet) { + let sequence = val.sequence; + if (sequence instanceof SetLeafSequence) { + data.nodes[id] = {name: `Set (${val.size})`}; + sequence.items.forEach(c => process(ref, c, id)); + } else { + invariant(sequence instanceof OrderedMetaSequence); + processMetaSequence(id, sequence, 'SetNode'); + } + } else if (val instanceof NomsMap) { + let sequence = val.sequence; + if (sequence instanceof MapLeafSequence) { + data.nodes[id] = {name: `Map (${val.size})`}; + sequence.items.forEach(entry => { + let k = entry.key; + let v = entry.value; + // TODO: handle non-string keys + let kid = process(ref, k, id); + if (kid) { + data.nodes[kid].isOpen = true; - process(ref, v, kid); - } else { - throw new Error('No kid id.'); - } - }); + process(ref, v, kid); + } else { + throw new Error('No kid id.'); + } + }); + } else { + invariant(sequence instanceof OrderedMetaSequence); + processMetaSequence(id, sequence, 'MapNode'); + } } else if (val instanceof Ref) { let refStr = val.toString(); data.nodes[id] = { @@ -114,45 +145,6 @@ function handleChunkLoad(ref: Ref, val: any, fromRef: ?string) { throw new Error('No kid id.'); } }); - } else if (val instanceof CompoundList) { - data.nodes[id] = {name: 'ListNode'}; - val.items.forEach(tuple => { - let kid = process(ref, formatKeyString(tuple.value), id); - if (kid) { - // Start map keys open, just makes it easier to use. - data.nodes[kid].isOpen = true; - - process(ref, tuple.ref, kid); - } else { - throw new Error('No kid id.'); - } - }); - } else if (val instanceof CompoundMap) { - data.nodes[id] = {name: 'MapNode'}; - val.items.forEach(tuple => { - let kid = process(ref, formatKeyString(tuple.value), id); - if (kid) { - // Start map keys open, just makes it easier to use. - data.nodes[kid].isOpen = true; - - process(ref, tuple.ref, kid); - } else { - throw new Error('No kid id.'); - } - }); - } else if (val instanceof CompoundSet) { - data.nodes[id] = {name: 'SetNode'}; - val.items.forEach(tuple => { - let kid = process(ref, formatKeyString(tuple.value), id); - if (kid) { - // Start map keys open, just makes it easier to use. - data.nodes[kid].isOpen = true; - - process(ref, tuple.ref, kid); - } else { - throw new Error('No kid id.'); - } - }); } return id; diff --git a/clients/tagshow/assert.js b/clients/tagshow/assert.js deleted file mode 100644 index 7f3e3cad97..0000000000 --- a/clients/tagshow/assert.js +++ /dev/null @@ -1,7 +0,0 @@ -// @flow - -export function invariant(exp: boolean, message: string = 'Invariant violated') { - if (!exp) { - throw new Error(message); - } -} diff --git a/clients/tagshow/datasetpicker.js b/clients/tagshow/datasetpicker.js index abf0450686..d8761c255a 100644 --- a/clients/tagshow/datasetpicker.js +++ b/clients/tagshow/datasetpicker.js @@ -1,7 +1,6 @@ // @flow -import {invariant} from './assert.js'; -import {readValue} from 'noms'; +import {invariant, readValue} from 'noms'; import eq from './eq.js'; import React from 'react'; import type {ChunkStore} from 'noms'; diff --git a/js/src/chunk_serializer.js b/js/src/chunk_serializer.js index 74e2f721b5..bcd09cbc1c 100644 --- a/js/src/chunk_serializer.js +++ b/js/src/chunk_serializer.js @@ -46,7 +46,7 @@ export function deserialize(buffer: ArrayBuffer): Array { invariant(buffer.byteLength - offset >= chunkHeaderSize, 'Invalid chunk buffer'); let refArray = new Uint8Array(buffer, offset, sha1Size); - let ref = new Ref(new Uint8Array(refArray)); + let ref = Ref.fromDigest(new Uint8Array(refArray)); offset += sha1Size; let sizeReadArray = new Uint8Array(buffer, offset, chunkLengthSize); diff --git a/js/src/collection.js b/js/src/collection.js new file mode 100644 index 0000000000..a7ad523d9d --- /dev/null +++ b/js/src/collection.js @@ -0,0 +1,17 @@ + // @flow + +import type {ChunkStore} from './chunk_store.js'; +import {Type} from './type.js'; +import {ValueBase} from './value.js'; +import type {Sequence} from './sequence.js'; // eslint-disable-line no-unused-vars + +export class Collection extends ValueBase { + sequence: S; + cs: ChunkStore; + + constructor(cs: ChunkStore, type: Type, sequence: S) { + super(type); + this.cs = cs; + this.sequence = sequence; + } +} diff --git a/js/src/decode.js b/js/src/decode.js index c134a2725e..1484f7827e 100644 --- a/js/src/decode.js +++ b/js/src/decode.js @@ -10,11 +10,11 @@ import {Field, makeCompoundType, makeEnumType, makePrimitiveType, makeStructType import {indexTypeForMetaSequence, MetaTuple, newMetaSequenceFromData} from './meta_sequence.js'; import {invariant, notNull} from './assert.js'; import {isPrimitiveKind, Kind} from './noms_kind.js'; -import {ListLeaf} from './list.js'; +import {ListLeafSequence, NomsList} from './list.js'; import {lookupPackage, Package, readPackage} from './package.js'; -import {MapLeaf} from './map.js'; +import {NomsMap, MapLeafSequence} from './map.js'; import {setDecodeNomsValue} from './read_value.js'; -import {SetLeaf} from './set.js'; +import {NomsSet, SetLeafSequence} from './set.js'; const typedTag = 't '; const blobTag = 'b '; @@ -135,17 +135,17 @@ class JsonArrayReader { return list; } - async readListLeaf(t: Type, pkg: ?Package): Promise { + async readListLeafSequence(t: Type, pkg: ?Package): Promise { let seq = await this.readSequence(t, pkg); - return new ListLeaf(this._cs, t, seq); + return new ListLeafSequence(t, seq); } - async readSetLeaf(t: Type, pkg: ?Package): Promise { + async readSetLeafSequence(t: Type, pkg: ?Package): Promise { let seq = await this.readSequence(t, pkg); - return new SetLeaf(this._cs, t, seq); + return new SetLeafSequence(t, seq); } - async readMapLeaf(t: Type, pkg: ?Package): Promise { + async readMapLeafSequence(t: Type, pkg: ?Package): Promise { let keyType = t.elemTypes[0]; let valueType = t.elemTypes[1]; let entries = []; @@ -155,24 +155,19 @@ class JsonArrayReader { entries.push({key: k, value: v}); } - return new MapLeaf(this._cs, t, entries); + return new MapLeafSequence(t, entries); } readEnum(): number { return this.readUint(); } - async maybeReadMetaSequence(t: Type, pkg: ?Package): Promise { - if (!this.readBool()) { - return null; - } - - let r2 = new JsonArrayReader(this.readArray(), this._cs); + async readMetaSequence(t: Type, pkg: ?Package): Promise { let data: Array = []; let indexType = indexTypeForMetaSequence(t); - while (!r2.atEnd()) { - let ref = r2.readRef(); - let v = await r2.readValueWithoutTag(indexType, pkg); + while (!this.atEnd()) { + let ref = this.readRef(); + let v = await this.readValueWithoutTag(indexType, pkg); data.push(new MetaTuple(ref, v)); } @@ -204,11 +199,9 @@ class JsonArrayReader { // TODO: Verify read values match tagged kinds. switch (t.kind) { case Kind.Blob: - let ms = await this.maybeReadMetaSequence(t, pkg); - if (ms) { - return ms; - } - + let isMeta = this.readBool(); + // https://github.com/attic-labs/noms/issues/798 + invariant(!isMeta, 'CompoundBlob not supported'); return this.readBlob(); case Kind.Bool: @@ -233,22 +226,20 @@ class JsonArrayReader { return this.readValueWithoutTag(t2, pkg); } case Kind.List: { - let ms = await this.maybeReadMetaSequence(t, pkg); - if (ms) { - return ms; - } - + let isMeta = this.readBool(); let r2 = new JsonArrayReader(this.readArray(), this._cs); - return r2.readListLeaf(t, pkg); + let sequence = isMeta ? + await r2.readMetaSequence(t, pkg) : + await r2.readListLeafSequence(t, pkg); + return new NomsList(this._cs, t, sequence); } case Kind.Map: { - let ms = await this.maybeReadMetaSequence(t, pkg); - if (ms) { - return ms; - } - + let isMeta = this.readBool(); let r2 = new JsonArrayReader(this.readArray(), this._cs); - return r2.readMapLeaf(t, pkg); + let sequence = isMeta ? + await r2.readMetaSequence(t, pkg) : + await r2.readMapLeafSequence(t, pkg); + return new NomsMap(this._cs, t, sequence); } case Kind.Package: return Promise.resolve(this.readPackage(t, pkg)); @@ -257,13 +248,12 @@ class JsonArrayReader { // for refs. return Promise.resolve(this.readRef()); case Kind.Set: { - let ms = await this.maybeReadMetaSequence(t, pkg); - if (ms) { - return ms; - } - + let isMeta = this.readBool(); let r2 = new JsonArrayReader(this.readArray(), this._cs); - return r2.readSetLeaf(t, pkg); + let sequence = isMeta ? + await r2.readMetaSequence(t, pkg) : + await r2.readSetLeafSequence(t, pkg); + return new NomsSet(this._cs, t, sequence); } case Kind.Enum: case Kind.Struct: diff --git a/js/src/decode_test.js b/js/src/decode_test.js index 27746e0f53..fb9aba9167 100644 --- a/js/src/decode_test.js +++ b/js/src/decode_test.js @@ -5,19 +5,21 @@ import MemoryStore from './memory_store.js'; import Ref from './ref.js'; import Struct from './struct.js'; import test from './async_test.js'; +import type {float64, int32, int64, uint8, uint16, uint32, uint64} from './primitives.js'; import type {TypeDesc} from './type.js'; import {assert} from 'chai'; import {decodeNomsValue, JsonArrayReader} from './decode.js'; import {Field, makeCompoundType, makeEnumType, makePrimitiveType, makeStructType, makeType, Type} from './type.js'; -import {invariant} from './assert.js'; +import {IndexedMetaSequence, MetaTuple} from './meta_sequence.js'; +import {invariant, notNull} from './assert.js'; import {Kind} from './noms_kind.js'; -import {ListLeaf, CompoundList} from './list.js'; -import {MapLeaf} from './map.js'; -import {MetaTuple} from './meta_sequence.js'; +import {ListLeafSequence, NomsList} from './list.js'; +import {MapLeafSequence, NomsMap} from './map.js'; +import {NomsSet, SetLeafSequence} from './set.js'; import {readValue} from './read_value.js'; import {registerPackage, Package} from './package.js'; -import {SetLeaf} from './set.js'; import {suite} from 'mocha'; +import {Value} from './value.js'; import {writeValue} from './encode.js'; suite('Decode', () => { @@ -92,11 +94,11 @@ suite('Decode', () => { let ms = new MemoryStore(); let a = [Kind.List, Kind.Int32, false, ['0', '1', '2', '3']]; let r = new JsonArrayReader(a, ms); - let v = await r.readTopLevelValue(); - invariant(v instanceof ListLeaf); + let v:NomsList = await r.readTopLevelValue(); + invariant(v instanceof NomsList); let tr = makeCompoundType(Kind.List, makePrimitiveType(Kind.Int32)); - let l = new ListLeaf(ms, tr, [0, 1, 2, 3]); + let l = new NomsList(ms, tr, new ListLeafSequence(tr, [0, 1, 2, 3])); assert.isTrue(l.equals(v)); }); @@ -105,8 +107,9 @@ suite('Decode', () => { let ms = new MemoryStore(); let a = [Kind.List, Kind.Value, false, [Kind.Int32, '1', Kind.String, 'hi', Kind.Bool, true]]; let r = new JsonArrayReader(a, ms); - let v = await r.readTopLevelValue(); - invariant(v instanceof ListLeaf); + let v:NomsList = await r.readTopLevelValue(); + invariant(v instanceof NomsList); + let tr = makeCompoundType(Kind.List, makePrimitiveType(Kind.Value)); assert.isTrue(v.type.equals(tr)); assert.strictEqual(1, await v.get(0)); @@ -119,10 +122,10 @@ suite('Decode', () => { let a = [Kind.Value, Kind.List, Kind.Int8, false, ['0', '1', '2']]; let r = new JsonArrayReader(a, ms); let v = await r.readTopLevelValue(); - invariant(v instanceof ListLeaf); + invariant(v instanceof NomsList); let tr = makeCompoundType(Kind.List, makePrimitiveType(Kind.Int8)); - let l = new ListLeaf(ms, tr, [0, 1, 2]); + let l = new NomsList(ms, tr, new ListLeafSequence(tr, [0, 1, 2])); assert.isTrue(l.equals(v)); }); @@ -130,20 +133,21 @@ suite('Decode', () => { let ms = new MemoryStore(); let ltr = makeCompoundType(Kind.List, makePrimitiveType(Kind.Int32)); - let r1 = writeValue(new ListLeaf(ms, ltr, [0, 1]), ltr, ms); - let r2 = writeValue(new ListLeaf(ms, ltr, [2, 3]), ltr, ms); - let r3 = writeValue(new ListLeaf(ms, ltr, [4, 5]), ltr, ms); + let r1 = writeValue(new NomsList(ms, ltr, new ListLeafSequence(ltr, [0, 1])), ltr, ms); + let r2 = writeValue(new NomsList(ms, ltr, new ListLeafSequence(ltr, [2, 3])), ltr, ms); + let r3 = writeValue(new NomsList(ms, ltr, new ListLeafSequence(ltr, [4, 5])), ltr, ms); let tuples = [ new MetaTuple(r1, 2), new MetaTuple(r2, 4), new MetaTuple(r3, 6) ]; - let l = new CompoundList(ms, ltr, tuples); + let l:NomsList = new NomsList(ms, ltr, new IndexedMetaSequence(ltr, tuples)); + invariant(l instanceof NomsList); let a = [Kind.List, Kind.Int32, true, [r1.toString(), '2', r2.toString(), '4', r3.toString(), '6']]; let r = new JsonArrayReader(a, ms); let v = await r.readTopLevelValue(); - invariant(v instanceof CompoundList); + invariant(v instanceof NomsList); assert.isTrue(v.ref.equals(l.ref)); }); @@ -151,11 +155,23 @@ suite('Decode', () => { let ms = new MemoryStore(); let a = [Kind.Map, Kind.Int64, Kind.Float64, false, ['0', '1', '2', '3']]; let r = new JsonArrayReader(a, ms); - let v = await r.readTopLevelValue(); - invariant(v instanceof MapLeaf); + let v:NomsMap = await r.readTopLevelValue(); + invariant(v instanceof NomsMap); let t = makeCompoundType(Kind.Map, makePrimitiveType(Kind.Int64), makePrimitiveType(Kind.Float64)); - let m = new MapLeaf(ms, t, [{key: 0, value: 1}, {key: 2, value: 3}]); + let m = new NomsMap(ms, t, new MapLeafSequence(t, [{key: 0, value: 1}, {key: 2, value: 3}])); + assert.isTrue(v.equals(m)); + }); + + test('read map of ref to uint64', async () => { + let ms = new MemoryStore(); + let a = [Kind.Map, Kind.Ref, Kind.Value, Kind.Uint64, false, ['sha1-0000000000000000000000000000000000000001', '2', 'sha1-0000000000000000000000000000000000000002', '4']]; + let r = new JsonArrayReader(a, ms); + let v:NomsMap = await r.readTopLevelValue(); + invariant(v instanceof NomsMap); + + let t = makeCompoundType(Kind.Map, makeCompoundType(Kind.Ref, makePrimitiveType(Kind.Value)), makePrimitiveType(Kind.Uint64)); + let m = new NomsMap(ms, t, new MapLeafSequence(t, [{key: new Ref('sha1-0000000000000000000000000000000000000001'), value: 2}, {key: new Ref('sha1-0000000000000000000000000000000000000002'), value: 4}])); assert.isTrue(v.equals(m)); }); @@ -163,11 +179,11 @@ suite('Decode', () => { let ms = new MemoryStore(); let a = [Kind.Value, Kind.Map, Kind.Uint64, Kind.Uint32, false, ['0', '1', '2', '3']]; let r = new JsonArrayReader(a, ms); - let v = await r.readTopLevelValue(); - invariant(v instanceof MapLeaf); + let v:NomsMap = await r.readTopLevelValue(); + invariant(v instanceof NomsMap); let t = makeCompoundType(Kind.Map, makePrimitiveType(Kind.Uint64), makePrimitiveType(Kind.Uint32)); - let m = new MapLeaf(ms, t, [{key: 0, value: 1}, {key: 2, value: 3}]); + let m = new NomsMap(ms, t, new MapLeafSequence(t, [{key: 0, value: 1}, {key: 2, value: 3}])); assert.isTrue(v.equals(m)); }); @@ -175,11 +191,11 @@ suite('Decode', () => { let ms = new MemoryStore(); let a = [Kind.Set, Kind.Uint8, false, ['0', '1', '2', '3']]; let r = new JsonArrayReader(a, ms); - let v = await r.readTopLevelValue(); - invariant(v instanceof SetLeaf); + let v:NomsSet = await r.readTopLevelValue(); + invariant(v instanceof NomsSet); let t = makeCompoundType(Kind.Set, makePrimitiveType(Kind.Uint8)); - let s = new SetLeaf(ms, t, [0, 1, 2, 3]); + let s = new NomsSet(ms, t, new SetLeafSequence(t, [0, 1, 2, 3])); assert.isTrue(v.equals(s)); }); @@ -187,14 +203,16 @@ suite('Decode', () => { let ms = new MemoryStore(); let a = [Kind.Value, Kind.Set, Kind.Uint16, false, ['0', '1', '2', '3']]; let r = new JsonArrayReader(a, ms); - let v = await r.readTopLevelValue(); + let v:NomsSet = await r.readTopLevelValue(); + invariant(v instanceof NomsSet); let t = makeCompoundType(Kind.Set, makePrimitiveType(Kind.Uint16)); - let s = new SetLeaf(ms, t, [0, 1, 2, 3]); + let s = new NomsSet(ms, t, new SetLeafSequence(t, [0, 1, 2, 3])); assert.isTrue(v.equals(s)); }); - function assertStruct(s: Struct, desc: TypeDesc, data: {[key: string]: any}) { + function assertStruct(s: ?Struct, desc: TypeDesc, data: {[key: string]: any}) { + notNull(s); invariant(s instanceof Struct, 'expected instanceof struct'); assert.deepEqual(desc, s.desc); @@ -287,7 +305,7 @@ suite('Decode', () => { assertStruct(v, tr.desc, { b: true, - l: new ListLeaf(ms, ltr, [0, 1, 2]), + l: new NomsList(ms, ltr, new ListLeafSequence(ltr, [0, 1, 2])), s: 'hi' }); }); @@ -399,11 +417,10 @@ suite('Decode', () => { let a = [Kind.Value, Kind.Map, Kind.String, Kind.Unresolved, pkg.ref.toString(), '0', false, ['bar', false, '2', 'baz', false, '1', 'foo', true, '3']]; let r = new JsonArrayReader(a, ms); - let v = await r.readTopLevelValue(); + let v:NomsMap = await r.readTopLevelValue(); + invariant(v instanceof NomsMap); - invariant(v instanceof MapLeaf); assert.strictEqual(3, v.size); - assertStruct(await v.get('foo'), tr.desc, {b: true, i: 3}); assertStruct(await v.get('bar'), tr.desc, {b: false, i: 2}); assertStruct(await v.get('baz'), tr.desc, {b: false, i: 1}); @@ -412,10 +429,10 @@ suite('Decode', () => { test('decodeNomsValue', async () => { let ms = new MemoryStore(); let chunk = Chunk.fromString(`t [${Kind.Value}, ${Kind.Set}, ${Kind.Uint16}, false, ["0", "1", "2", "3"]]`); - let v = await decodeNomsValue(chunk, new MemoryStore()); + let v:NomsSet = await decodeNomsValue(chunk, new MemoryStore()); let t = makeCompoundType(Kind.Set, makePrimitiveType(Kind.Uint16)); - let s = new SetLeaf(ms, t, [0, 1, 2, 3]); + let s:NomsSet = new NomsSet(ms, t, new SetLeafSequence(t, [0, 1, 2, 3])); assert.isTrue(v.equals(s)); }); diff --git a/js/src/encode.js b/js/src/encode.js index 2eb8aaff7c..50c2737a24 100644 --- a/js/src/encode.js +++ b/js/src/encode.js @@ -6,16 +6,16 @@ import Struct from './struct.js'; import type {ChunkStore} from './chunk_store.js'; import type {NomsKind} from './noms_kind.js'; import {encode as encodeBase64} from './base64.js'; +import {EnumDesc, makePrimitiveType, StructDesc, Type} from './type.js'; import {indexTypeForMetaSequence} from './meta_sequence.js'; import {invariant, notNull} from './assert.js'; import {isPrimitiveKind, Kind} from './noms_kind.js'; -import {ListLeaf} from './list.js'; +import {ListLeafSequence, NomsList} from './list.js'; import {lookupPackage, Package} from './package.js'; -import {makePrimitiveType, EnumDesc, StructDesc, Type} from './type.js'; -import {MapLeaf} from './map.js'; +import {MapLeafSequence, NomsMap} from './map.js'; +import {NomsSet, SetLeafSequence} from './set.js'; import {Sequence} from './sequence.js'; import {setEncodeNomsValue} from './get_ref.js'; -import {SetLeaf} from './set.js'; const typedTag = 't '; @@ -139,29 +139,31 @@ class JsonArrayWriter { this.writeInt(v); // TODO: Verify value fits in type break; case Kind.List: { - invariant(v instanceof Sequence); - if (this.maybeWriteMetaSequence(v, t, pkg)) { + invariant(v instanceof NomsList); + let sequence = v.sequence; + if (this.maybeWriteMetaSequence(sequence, t, pkg)) { break; } - invariant(v instanceof ListLeaf); + invariant(sequence instanceof ListLeafSequence); let w2 = new JsonArrayWriter(this._cs); let elemType = t.elemTypes[0]; - v.items.forEach(sv => w2.writeValue(sv, elemType)); + sequence.items.forEach(sv => w2.writeValue(sv, elemType)); this.write(w2.array); break; } case Kind.Map: { - invariant(v instanceof Sequence); - if (this.maybeWriteMetaSequence(v, t, pkg)) { + invariant(v instanceof NomsMap); + let sequence = v.sequence; + if (this.maybeWriteMetaSequence(sequence, t, pkg)) { break; } - invariant(v instanceof MapLeaf); + invariant(sequence instanceof MapLeafSequence); let w2 = new JsonArrayWriter(this._cs); let keyType = t.elemTypes[0]; let valueType = t.elemTypes[1]; - v.items.forEach(entry => { + sequence.items.forEach(entry => { w2.writeValue(entry.key, keyType); w2.writeValue(entry.value, valueType); }); @@ -187,16 +189,17 @@ class JsonArrayWriter { break; } case Kind.Set: { - invariant(v instanceof Sequence); - if (this.maybeWriteMetaSequence(v, t, pkg)) { + invariant(v instanceof NomsSet); + let sequence = v.sequence; + if (this.maybeWriteMetaSequence(sequence, t, pkg)) { break; } - invariant(v instanceof SetLeaf); + invariant(sequence instanceof SetLeafSequence); let w2 = new JsonArrayWriter(this._cs); let elemType = t.elemTypes[0]; let elems = []; - v.items.forEach(v => { + sequence.items.forEach(v => { elems.push(v); }); elems.forEach(elem => w2.writeValue(elem, elemType)); diff --git a/js/src/encode_test.js b/js/src/encode_test.js index 214489938a..1c433fa240 100644 --- a/js/src/encode_test.js +++ b/js/src/encode_test.js @@ -8,14 +8,14 @@ import Ref from './ref.js'; import Struct from './struct.js'; import test from './async_test.js'; import type {NomsKind} from './noms_kind.js'; +import {encodeNomsValue, JsonArrayWriter} from './encode.js'; import {Field, makeCompoundType, makeEnumType, makePrimitiveType, makeStructType, makeType, Type} from './type.js'; -import {JsonArrayWriter, encodeNomsValue} from './encode.js'; +import {IndexedMetaSequence, MetaTuple} from './meta_sequence.js'; import {Kind} from './noms_kind.js'; -import {ListLeaf, CompoundList} from './list.js'; -import {MapLeaf} from './map.js'; -import {MetaTuple} from './meta_sequence.js'; +import {ListLeafSequence, NomsList} from './list.js'; +import {MapLeafSequence, NomsMap} from './map.js'; +import {NomsSet, SetLeafSequence} from './set.js'; import {Package, registerPackage} from './package.js'; -import {SetLeaf} from './set.js'; import {writeValue} from './encode.js'; suite('Encode', () => { @@ -61,7 +61,7 @@ suite('Encode', () => { let w = new JsonArrayWriter(ms); let tr = makeCompoundType(Kind.List, makePrimitiveType(Kind.Int32)); - let l = new ListLeaf(ms, tr, [0, 1, 2, 3]); + let l = new NomsList(ms, tr, new ListLeafSequence(tr, [0, 1, 2, 3])); w.writeTopLevel(tr, l); assert.deepEqual([Kind.List, Kind.Int32, false, ['0', '1', '2', '3']], w.array); }); @@ -72,7 +72,10 @@ suite('Encode', () => { let it = makeCompoundType(Kind.List, makePrimitiveType(Kind.Int16)); let tr = makeCompoundType(Kind.List, it); - let v = new ListLeaf(ms, tr, [new ListLeaf(ms, it, [0]), new ListLeaf(ms, it, [1, 2, 3])]); + let v = new NomsList(ms, tr, new ListLeafSequence(tr, [ + new NomsList(ms, tr, new ListLeafSequence(it, [0])), + new NomsList(ms, tr, new ListLeafSequence(it, [1, 2, 3])) + ])); w.writeTopLevel(tr, v); assert.deepEqual([Kind.List, Kind.List, Kind.Int16, false, [false, ['0'], false, ['1', '2', '3']]], w.array); }); @@ -82,7 +85,7 @@ suite('Encode', () => { let w = new JsonArrayWriter(ms); let tr = makeCompoundType(Kind.Set, makePrimitiveType(Kind.Uint32)); - let v = new SetLeaf(ms, tr, [0, 1, 2, 3]); + let v = new NomsSet(ms, tr, new SetLeafSequence(tr, [0, 1, 2, 3])); w.writeTopLevel(tr, v); assert.deepEqual([Kind.Set, Kind.Uint32, false, ['0', '1', '2', '3']], w.array); }); @@ -93,7 +96,10 @@ suite('Encode', () => { let st = makeCompoundType(Kind.Set, makePrimitiveType(Kind.Int32)); let tr = makeCompoundType(Kind.Set, st); - let v = new SetLeaf(ms, tr, [new SetLeaf(ms, st, [0]), new SetLeaf(ms, st, [1, 2, 3])]); + let v = new NomsSet(ms, tr, new SetLeafSequence(tr, [ + new NomsSet(ms, tr, new SetLeafSequence(st, [0])), + new NomsSet(ms, tr, new SetLeafSequence(st, [1, 2, 3])) + ])); w.writeTopLevel(tr, v); assert.deepEqual([Kind.Set, Kind.Set, Kind.Int32, false, [false, ['0'], false, ['1', '2', '3']]], w.array); @@ -104,7 +110,7 @@ suite('Encode', () => { let w = new JsonArrayWriter(ms); let tr = makeCompoundType(Kind.Map, makePrimitiveType(Kind.String), makePrimitiveType(Kind.Bool)); - let v = new MapLeaf(ms, tr, [{key: 'a', value: false}, {key:'b', value:true}]); + let v = new NomsMap(ms, tr, new MapLeafSequence(tr, [{key: 'a', value: false}, {key:'b', value:true}])); w.writeTopLevel(tr, v); assert.deepEqual([Kind.Map, Kind.String, Kind.Bool, false, ['a', false, 'b', true]], w.array); }); @@ -117,10 +123,9 @@ suite('Encode', () => { let vt = makeCompoundType(Kind.Set, makePrimitiveType(Kind.Bool)); let tr = makeCompoundType(Kind.Map, kt, vt); - - let s = new SetLeaf(ms, vt, [true]); - let m1 = new MapLeaf(ms, kt, [{key: 'a', value: 0}]); - let v = new MapLeaf(ms, tr, [{key: m1, value: s}]); + let s = new NomsSet(ms, vt, new SetLeafSequence(vt, [true])); + let m1 = new NomsMap(ms, kt, new MapLeafSequence(kt, [{key: 'a', value: 0}])); + let v = new NomsMap(ms, kt, new MapLeafSequence(tr, [{key: m1, value: s}])); w.writeTopLevel(tr, v); assert.deepEqual([Kind.Map, Kind.Map, Kind.String, Kind.Int64, Kind.Set, Kind.Bool, false, [false, ['a', '0'], false, [true]]], w.array); }); @@ -221,11 +226,11 @@ suite('Encode', () => { let pkgRef = pkg.ref; let type = makeType(pkgRef, 0); - let v = new Struct(type, typeDef, {l: new ListLeaf(ms, ltr, ['a', 'b'])}); + let v = new Struct(type, typeDef, {l: new NomsList(ms, ltr, new ListLeafSequence(ltr, ['a', 'b']))}); w.writeTopLevel(type, v); assert.deepEqual([Kind.Unresolved, pkgRef.toString(), '0', false, ['a', 'b']], w.array); - v = new Struct(type, typeDef, {l: new ListLeaf(ms, ltr, [])}); + v = new Struct(type, typeDef, {l: new NomsList(ms, ltr, new ListLeafSequence(ltr, []))}); w = new JsonArrayWriter(ms); w.writeTopLevel(type, v); assert.deepEqual([Kind.Unresolved, pkgRef.toString(), '0', false, []], w.array); @@ -275,7 +280,7 @@ suite('Encode', () => { let pkgRef = pkg.ref; let typ = makeType(pkgRef, 0); let listType = makeCompoundType(Kind.List, typ); - let l = new ListLeaf(ms, listType, [0, 1, 2]); + let l = new NomsList(ms, listType, new ListLeafSequence(listType, [0, 1, 2])); w.writeTopLevel(listType, l); assert.deepEqual([Kind.List, Kind.Unresolved, pkgRef.toString(), '0', false, ['0', '1', '2']], w.array); @@ -286,15 +291,15 @@ suite('Encode', () => { let w = new JsonArrayWriter(ms); let ltr = makeCompoundType(Kind.List, makePrimitiveType(Kind.Int32)); - let r1 = writeValue(new ListLeaf(ms, ltr, [0, 1]), ltr, ms); - let r2 = writeValue(new ListLeaf(ms, ltr, [2, 3]), ltr, ms); - let r3 = writeValue(new ListLeaf(ms, ltr, [4, 5]), ltr, ms); + let r1 = writeValue(new NomsList(ms, ltr, new ListLeafSequence(ltr, [0, 1])), ltr, ms); + let r2 = writeValue(new NomsList(ms, ltr, new ListLeafSequence(ltr, [2, 3])), ltr, ms); + let r3 = writeValue(new NomsList(ms, ltr, new ListLeafSequence(ltr, [4, 5])), ltr, ms); let tuples = [ new MetaTuple(r1, 2), new MetaTuple(r2, 4), new MetaTuple(r3, 6) ]; - let l = new CompoundList(ms, ltr, tuples); + let l = new NomsList(ms, ltr, new IndexedMetaSequence(ltr, tuples)); w.writeTopLevel(ltr, l); assert.deepEqual([Kind.List, Kind.Int32, true, [r1.toString(), '2', r2.toString(), '4', r3.toString(), '6']], w.array); diff --git a/js/src/indexed_sequence.js b/js/src/indexed_sequence.js index 6756643723..0853a74955 100644 --- a/js/src/indexed_sequence.js +++ b/js/src/indexed_sequence.js @@ -1,5 +1,6 @@ // @flow +import type {ChunkStore} from './chunk_store.js'; import {notNull} from './assert.js'; import {search, Sequence, SequenceCursor} from './sequence.js'; @@ -8,12 +9,12 @@ export class IndexedSequence extends Sequence { throw new Error('override'); } - async newCursorAt(idx: number): Promise { + async newCursorAt(cs: ChunkStore, idx: number): Promise { let cursor: ?IndexedSequenceCursor = null; let sequence: ?IndexedSequence = this; while (sequence) { - cursor = new IndexedSequenceCursor(cursor, sequence, 0); + cursor = new IndexedSequenceCursor(cs, cursor, sequence, 0); idx -= cursor.advanceToOffset(idx); sequence = await cursor.getChildSequence(); } diff --git a/js/src/list.js b/js/src/list.js index 17b5427b81..3bf2f66ad2 100644 --- a/js/src/list.js +++ b/js/src/list.js @@ -1,22 +1,18 @@ // @flow -import type {ChunkStore} from './chunk_store.js'; import type {valueOrPrimitive} from './value.js'; // eslint-disable-line no-unused-vars +import {Collection} from './collection.js'; import {IndexedSequence} from './indexed_sequence.js'; -import {invariant} from './assert.js'; -import {Kind} from './noms_kind.js'; -import {MetaTuple, registerMetaValue} from './meta_sequence.js'; -import {Type} from './type.js'; -export class NomsList extends IndexedSequence { - async get(idx: number): Promise { - invariant(idx < this.length, idx + ' >= ' + this.length); - let cursor = await this.newCursorAt(idx); +export class NomsList extends Collection { + async get(idx: number): Promise { + // TODO (when |length| works) invariant(idx < this.length, idx + ' >= ' + this.length); + let cursor = await this.sequence.newCursorAt(this.cs, idx); return cursor.getCurrent(); } - async forEach(cb: (v: K, i: number) => void): Promise { - let cursor = await this.newCursorAt(0); + async forEach(cb: (v: T, i: number) => void): Promise { + let cursor = await this.sequence.newCursorAt(this.cs, 0); return cursor.iter((v, i) => { cb(v, i); return false; @@ -24,46 +20,16 @@ export class NomsList extends IndexedSequence { } get length(): number { - return this.items.length; + if (this.sequence instanceof ListLeafSequence) { + return this.sequence.items.length; + } + + throw new Error('not implemented'); } } -export class ListLeaf extends NomsList { +export class ListLeafSequence extends IndexedSequence { getOffset(idx: number): number { return idx; } } - -export class CompoundList extends NomsList> { - offsets: Array; - - constructor(cs: ChunkStore, type: Type, items: Array>) { - super(cs, type, items); - this.isMeta = true; - this.offsets = []; - let cum = 0; - for (let i = 0; i < items.length; i++) { - let length = items[i].value; - this.offsets.push(cum + length - 1); - cum += length; - } - } - - getOffset(idx: number): number { - return this.offsets[idx]; - } - - async getChildSequence(idx: number): Promise { - let mt = this.items[idx]; - let ms = await mt.readValue(this.cs); - invariant(ms instanceof NomsList); - return ms; - } - - get length(): number { - return this.offsets[this.items.length - 1] + 1; - } -} - -registerMetaValue(Kind.List, (cs, type, tuples) => new CompoundList(cs, type, tuples)); - diff --git a/js/src/list_test.js b/js/src/list_test.js index a7ad4eb384..ef0fe6d728 100644 --- a/js/src/list_test.js +++ b/js/src/list_test.js @@ -5,17 +5,17 @@ import {suite} from 'mocha'; import MemoryStore from './memory_store.js'; import test from './async_test.js'; -import {CompoundList, ListLeaf} from './list.js'; +import {IndexedMetaSequence, MetaTuple} from './meta_sequence.js'; import {Kind} from './noms_kind.js'; +import {ListLeafSequence, NomsList} from './list.js'; import {makeCompoundType, makePrimitiveType} from './type.js'; -import {MetaTuple} from './meta_sequence.js'; import {writeValue} from './encode.js'; -suite('ListLeaf', () => { +suite('ListLeafSequence', () => { test('get', async () => { let ms = new MemoryStore(); let tr = makeCompoundType(Kind.List, makePrimitiveType(Kind.String)); - let l = new ListLeaf(ms, tr, ['z', 'x', 'a', 'b']); + let l = new NomsList(ms, tr, new ListLeafSequence(tr, ['z', 'x', 'a', 'b'])); assert.strictEqual('z', await l.get(0)); assert.strictEqual('x', await l.get(1)); assert.strictEqual('a', await l.get(2)); @@ -25,7 +25,7 @@ suite('ListLeaf', () => { test('forEach', async () => { let ms = new MemoryStore(); let tr = makeCompoundType(Kind.List, makePrimitiveType(Kind.Int32)); - let l = new ListLeaf(ms, tr, [4, 2, 10, 16]); + let l = new NomsList(ms, tr, new ListLeafSequence(tr, [4, 2, 10, 16])); let values = []; await l.forEach((v, i) => { values.push(v, i); }); @@ -34,24 +34,24 @@ suite('ListLeaf', () => { }); suite('CompoundList', () => { - function build(): CompoundList { + function build(): NomsList { let ms = new MemoryStore(); let tr = makeCompoundType(Kind.List, makePrimitiveType(Kind.String)); - let l1 = new ListLeaf(ms, tr, ['a', 'b']); + let l1 = new NomsList(ms, tr, new ListLeafSequence(tr, ['a', 'b'])); let r1 = writeValue(l1, tr, ms); - let l2 = new ListLeaf(ms, tr, ['e', 'f']); + let l2 = new NomsList(ms, tr, new ListLeafSequence(tr, ['e', 'f'])); let r2 = writeValue(l2, tr, ms); - let l3 = new ListLeaf(ms, tr, ['h', 'i']); + let l3 = new NomsList(ms, tr, new ListLeafSequence(tr, ['h', 'i'])); let r3 = writeValue(l3, tr, ms); - let l4 = new ListLeaf(ms, tr, ['m', 'n']); + let l4 = new NomsList(ms, tr, new ListLeafSequence(tr, ['m', 'n'])); let r4 = writeValue(l4, tr, ms); - let m1 = new CompoundList(ms, tr, [new MetaTuple(r1, 2), new MetaTuple(r2, 2)]); + let m1 = new NomsList(ms, tr, new IndexedMetaSequence(tr, [new MetaTuple(r1, 2), new MetaTuple(r2, 2)])); let rm1 = writeValue(m1, tr, ms); - let m2 = new CompoundList(ms, tr, [new MetaTuple(r3, 2), new MetaTuple(r4, 2)]); + let m2 = new NomsList(ms, tr, new IndexedMetaSequence(tr, [new MetaTuple(r3, 2), new MetaTuple(r4, 2)])); let rm2 = writeValue(m2, tr, ms); - let l = new CompoundList(ms, tr, [new MetaTuple(rm1, 4), new MetaTuple(rm2, 4)]); + let l = new NomsList(ms, tr, new IndexedMetaSequence(tr, [new MetaTuple(rm1, 4), new MetaTuple(rm2, 4)])); return l; } diff --git a/js/src/map.js b/js/src/map.js index 0c5e019406..94956497e2 100644 --- a/js/src/map.js +++ b/js/src/map.js @@ -1,22 +1,23 @@ // @flow -import type {ChunkStore} from './chunk_store.js'; import type {valueOrPrimitive} from './value.js'; // eslint-disable-line no-unused-vars +import {Collection} from './collection.js'; import {equals} from './value.js'; -import {invariant} from './assert.js'; -import {Kind} from './noms_kind.js'; -import {MetaTuple, registerMetaValue} from './meta_sequence.js'; import {OrderedSequence} from './ordered_sequence.js'; -import {Type} from './type.js'; type Entry = { key: K, value: V }; -export class NomsMap extends OrderedSequence { +export class NomsMap extends Collection { + async has(key: K): Promise { + let cursor = await this.sequence.newCursorAt(this.cs, key); + return cursor.valid && equals(cursor.getCurrentKey(), key); + } + async first(): Promise { - let cursor = await this.newCursorAt(null); + let cursor = await this.sequence.newCursorAt(this.cs, null); if (!cursor.valid) { return undefined; } @@ -26,7 +27,7 @@ export class NomsMap extends Ordere } async get(key: K): Promise { - let cursor = await this.newCursorAt(key); + let cursor = await this.sequence.newCursorAt(this.cs, key); if (!cursor.valid) { return undefined; } @@ -36,7 +37,7 @@ export class NomsMap extends Ordere } async forEach(cb: (v: V, k: K) => void): Promise { - let cursor = await this.newCursorAt(null); + let cursor = await this.sequence.newCursorAt(this.cs, null); return cursor.iter(entry => { cb(entry.value, entry.key); return false; @@ -44,37 +45,16 @@ export class NomsMap extends Ordere } get size(): number { - return this.items.length; + if (this.sequence instanceof MapLeafSequence) { + return this.sequence.items.length; + } + + throw new Error('Not implemented'); } } -export class MapLeaf extends NomsMap> { +export class MapLeafSequence extends OrderedSequence> { getKey(idx: number): K { return this.items[idx].key; } } - -export class CompoundMap extends NomsMap> { - constructor(cs: ChunkStore, type: Type, items: Array) { - super(cs, type, items); - this.isMeta = true; - } - - getKey(idx: number): K { - return this.items[idx].value; - } - - async getChildSequence(idx: number): Promise { - let mt = this.items[idx]; - let ms = await mt.readValue(this.cs); - invariant(ms instanceof NomsMap); - return ms; - } - - get size(): number { - throw new Error('not implemented'); - } -} - -registerMetaValue(Kind.Map, (cs, type, tuples) => new CompoundMap(cs, type, tuples)); - diff --git a/js/src/map_test.js b/js/src/map_test.js index ad8380955b..3b52031bcd 100644 --- a/js/src/map_test.js +++ b/js/src/map_test.js @@ -5,17 +5,18 @@ import {suite} from 'mocha'; import MemoryStore from './memory_store.js'; import test from './async_test.js'; -import {CompoundMap, MapLeaf} from './map.js'; +import type {ChunkStore} from './chunk_store.js'; import {Kind} from './noms_kind.js'; import {makeCompoundType, makePrimitiveType} from './type.js'; -import {MetaTuple} from './meta_sequence.js'; +import {MapLeafSequence, NomsMap} from './map.js'; +import {MetaTuple, OrderedMetaSequence} from './meta_sequence.js'; import {writeValue} from './encode.js'; suite('MapLeaf', () => { test('has', async () => { let ms = new MemoryStore(); let tr = makeCompoundType(Kind.Map, makePrimitiveType(Kind.String), makePrimitiveType(Kind.Bool)); - let m = new MapLeaf(ms, tr, [{key: 'a', value: false}, {key:'k', value:true}]); + let m = new NomsMap(ms, tr, new MapLeafSequence(tr, [{key: 'a', value: false}, {key:'k', value:true}])); assert.isTrue(await m.has('a')); assert.isFalse(await m.has('b')); assert.isTrue(await m.has('k')); @@ -25,7 +26,7 @@ suite('MapLeaf', () => { test('first/get', async () => { let ms = new MemoryStore(); let tr = makeCompoundType(Kind.Map, makePrimitiveType(Kind.String), makePrimitiveType(Kind.Int32)); - let m = new MapLeaf(ms, tr, [{key: 'a', value: 4}, {key:'k', value:8}]); + let m = new NomsMap(ms, tr, new MapLeafSequence(tr, [{key: 'a', value: 4}, {key:'k', value:8}])); assert.deepEqual(['a', 4], await m.first()); @@ -38,7 +39,7 @@ suite('MapLeaf', () => { test('forEach', async () => { let ms = new MemoryStore(); let tr = makeCompoundType(Kind.Map, makePrimitiveType(Kind.String), makePrimitiveType(Kind.Int32)); - let m = new MapLeaf(ms, tr, [{key: 'a', value: 4}, {key:'k', value:8}]); + let m = new NomsMap(ms, tr, new MapLeafSequence(tr, [{key: 'a', value: 4}, {key:'k', value:8}])); let kv = []; await m.forEach((v, k) => { kv.push(k, v); }); @@ -47,29 +48,29 @@ suite('MapLeaf', () => { }); suite('CompoundMap', () => { - function build(): Array { - let ms = new MemoryStore(); + function build(cs: ChunkStore): Array { let tr = makeCompoundType(Kind.Map, makePrimitiveType(Kind.String), makePrimitiveType(Kind.Bool)); - let l1 = new MapLeaf(ms, tr, [{key: 'a', value: false}, {key:'b', value:false}]); - let r1 = writeValue(l1, tr, ms); - let l2 = new MapLeaf(ms, tr, [{key: 'e', value: true}, {key:'f', value:true}]); - let r2 = writeValue(l2, tr, ms); - let l3 = new MapLeaf(ms, tr, [{key: 'h', value: false}, {key:'i', value:true}]); - let r3 = writeValue(l3, tr, ms); - let l4 = new MapLeaf(ms, tr, [{key: 'm', value: true}, {key:'n', value:false}]); - let r4 = writeValue(l4, tr, ms); + let l1 = new NomsMap(cs, tr, new MapLeafSequence(tr, [{key: 'a', value: false}, {key:'b', value:false}])); + let r1 = writeValue(l1, tr, cs); + let l2 = new NomsMap(cs, tr, new MapLeafSequence(tr, [{key: 'e', value: true}, {key:'f', value:true}])); + let r2 = writeValue(l2, tr, cs); + let l3 = new NomsMap(cs, tr, new MapLeafSequence(tr, [{key: 'h', value: false}, {key:'i', value:true}])); + let r3 = writeValue(l3, tr, cs); + let l4 = new NomsMap(cs, tr, new MapLeafSequence(tr, [{key: 'm', value: true}, {key:'n', value:false}])); + let r4 = writeValue(l4, tr, cs); - let m1 = new CompoundMap(ms, tr, [new MetaTuple(r1, 'b'), new MetaTuple(r2, 'f')]); - let rm1 = writeValue(m1, tr, ms); - let m2 = new CompoundMap(ms, tr, [new MetaTuple(r3, 'i'), new MetaTuple(r4, 'n')]); - let rm2 = writeValue(m2, tr, ms); + let m1 = new NomsMap(cs, tr, new OrderedMetaSequence(tr, [new MetaTuple(r1, 'b'), new MetaTuple(r2, 'f')])); + let rm1 = writeValue(m1, tr, cs); + let m2 = new NomsMap(cs, tr, new OrderedMetaSequence(tr, [new MetaTuple(r3, 'i'), new MetaTuple(r4, 'n')])); + let rm2 = writeValue(m2, tr, cs); - let c = new CompoundMap(ms, tr, [new MetaTuple(rm1, 'f'), new MetaTuple(rm2, 'n')]); + let c = new NomsMap(cs, tr, new OrderedMetaSequence(tr, [new MetaTuple(rm1, 'f'), new MetaTuple(rm2, 'n')])); return [c, m1, m2]; } test('get', async () => { - let [c] = build(); + let ms = new MemoryStore(); + let [c] = build(ms); assert.strictEqual(false, await c.get('a')); assert.strictEqual(false, await c.get('b')); @@ -88,8 +89,8 @@ suite('CompoundMap', () => { }); test('first/has', async () => { - let [c, m1, m2] = build(); - + let ms = new MemoryStore(); + let [c, m1, m2] = build(ms); assert.deepEqual(['a', false], await c.first()); assert.deepEqual(['a', false], await m1.first()); @@ -112,7 +113,8 @@ suite('CompoundMap', () => { }); test('forEach', async () => { - let [c] = build(); + let ms = new MemoryStore(); + let [c] = build(ms); let kv = []; await c.forEach((v, k) => { kv.push(k, v); }); diff --git a/js/src/meta_sequence.js b/js/src/meta_sequence.js index 92b861cb61..8016587ad9 100644 --- a/js/src/meta_sequence.js +++ b/js/src/meta_sequence.js @@ -2,10 +2,12 @@ import Ref from './ref.js'; import type {ChunkStore} from './chunk_store.js'; -import type {NomsKind} from './noms_kind.js'; +import type {valueOrPrimitive} from './value.js'; // eslint-disable-line no-unused-vars import {CompoundDesc, makeCompoundType, makePrimitiveType, Type} from './type.js'; -import {invariant, notNull} from './assert.js'; +import {IndexedSequence} from './indexed_sequence.js'; +import {invariant} from './assert.js'; import {Kind} from './noms_kind.js'; +import {OrderedSequence} from './ordered_sequence.js'; import {readValue} from './read_value.js'; import {Sequence} from './sequence.js'; @@ -19,23 +21,73 @@ export class MetaTuple { this.ref = ref; this.value = value; } +} - readValue(cs: ChunkStore): Promise { - return readValue(this.ref, cs); +export class IndexedMetaSequence extends IndexedSequence> { + offsets: Array; + + constructor(type: Type, items: Array>) { + super(type, items); + this.isMeta = true; + this.offsets = []; + let cum = 0; + for (let i = 0; i < items.length; i++) { + let length = items[i].value; + this.offsets.push(cum + length - 1); + cum += length; + } + } + + async getChildSequence(cs: ChunkStore, idx: number): Promise { + if (!this.isMeta) { + return null; + } + + let mt = this.items[idx]; + let collection = await readValue(mt.ref, cs); + invariant(collection && collection.sequence instanceof IndexedSequence); + return collection.sequence; + } + + getOffset(idx: number): number { + return this.offsets[idx]; } } -export type metaBuilderFn = (cs: ChunkStore, t: Type, tuples: Array) => MetaSequence; +export class OrderedMetaSequence extends OrderedSequence> { + constructor(type: Type, items: Array) { + super(type, items); + this.isMeta = true; + } -let metaFuncMap: Map = new Map(); + async getChildSequence(cs: ChunkStore, idx: number): Promise { + if (!this.isMeta) { + return null; + } -export function newMetaSequenceFromData(cs: ChunkStore, t: Type, data: Array): MetaSequence { - let ctor = notNull(metaFuncMap.get(t.kind)); - return ctor(cs, t, data); + let mt = this.items[idx]; + let collection = await readValue(mt.ref, cs); + invariant(collection && collection.sequence instanceof OrderedSequence); + return collection.sequence; + } + + getKey(idx: number): K { + return this.items[idx].value; + } } -export function registerMetaValue(k: NomsKind, bf: metaBuilderFn) { - metaFuncMap.set(k, bf); +export function newMetaSequenceFromData(cs: ChunkStore, type: Type, tuples: Array): MetaSequence { + switch (type.kind) { + case Kind.Map: + case Kind.Set: + return new OrderedMetaSequence(type, tuples); + case Kind.List: + return new IndexedMetaSequence(type, tuples); + case Kind.Blob: + throw new Error('Not implemented'); + default: + throw new Error('Not reached'); + } } let indexedSequenceIndexType = makePrimitiveType(Kind.Uint64); diff --git a/js/src/noms.js b/js/src/noms.js index f6c20eaa26..ebe29f7651 100644 --- a/js/src/noms.js +++ b/js/src/noms.js @@ -7,10 +7,13 @@ export {default as MemoryStore} from './memory_store.js'; export {default as Ref} from './ref.js'; export {default as Struct} from './struct.js'; export {encodeNomsValue} from './encode.js'; +export {invariant, notNull} from './assert.js'; +export {isPrimitiveKind, Kind} from './noms_kind.js'; +export {ListLeafSequence, NomsList} from './list.js'; export {lookupPackage, Package, readPackage, registerPackage} from './package.js'; -export {NomsList, ListLeaf, CompoundList} from './list.js'; -export {NomsMap, MapLeaf, CompoundMap} from './map.js'; -export {NomsSet, SetLeaf, CompoundSet} from './set.js'; +export {NomsMap, MapLeafSequence} from './map.js'; +export {NomsSet, SetLeafSequence} from './set.js'; +export {OrderedMetaSequence, IndexedMetaSequence} from './meta_sequence.js'; export {readValue} from './read_value.js'; export { CompoundDesc, @@ -22,11 +25,11 @@ export { makeStructType, makeType, makeUnresolvedType, + packageType, PrimitiveDesc, StructDesc, Type, typeType, - packageType, UnresolvedDesc } from './type.js'; diff --git a/js/src/ordered_sequence.js b/js/src/ordered_sequence.js index f9fb2b3383..cfd799e830 100644 --- a/js/src/ordered_sequence.js +++ b/js/src/ordered_sequence.js @@ -1,8 +1,9 @@ // @flow +import type {ChunkStore} from './chunk_store.js'; import type {valueOrPrimitive} from './value.js'; // eslint-disable-line no-unused-vars import {invariant, notNull} from './assert.js'; -import {less, equals} from './value.js'; +import {less} from './value.js'; import {search, Sequence, SequenceCursor} from './sequence.js'; export class OrderedSequence extends Sequence { @@ -12,12 +13,12 @@ export class OrderedSequence extends Sequence { // -cursor positioned at // -first value, if |key| is null // -first value >= |key| - async newCursorAt(key: ?K): Promise { + async newCursorAt(cs: ChunkStore, key: ?K): Promise { let cursor: ?OrderedSequenceCursor = null; let sequence: ?OrderedSequence = this; while (sequence) { - cursor = new OrderedSequenceCursor(cursor, sequence, 0); + cursor = new OrderedSequenceCursor(cs, cursor, sequence, 0); if (key) { if (!cursor._seekTo(key)) { return cursor; // invalid @@ -33,11 +34,6 @@ export class OrderedSequence extends Sequence { getKey(idx: number): K { // eslint-disable-line no-unused-vars throw new Error('override'); } - - async has(key: K): Promise { - let cursor = await this.newCursorAt(key); - return cursor.valid && equals(cursor.getCurrentKey(), key); - } } export class OrderedSequenceCursor extends SequenceCursor { diff --git a/js/src/ref.js b/js/src/ref.js index 50a381fbb9..e07df242d4 100644 --- a/js/src/ref.js +++ b/js/src/ref.js @@ -2,9 +2,15 @@ import Rusha from 'rusha'; +import type {Value} from './value.js'; +import {invariant} from './assert.js'; + const r = new Rusha(); const sha1Size = 20; -const pattern = /^sha1-([0-9a-f]{40})$/; +const pattern = /^(sha1-[0-9a-f]{40})$/; + +const sha1Prefix = 'sha1-'; +const emtpyRefStr = sha1Prefix + '0'.repeat(40); function uint8ArrayToHex(a: Uint8Array): string { let hex = ''; @@ -31,46 +37,36 @@ function hexToUint8(s: string): Uint8Array { } export default class Ref { - digest: Uint8Array; + _refStr: string; - constructor(digest: Uint8Array = new Uint8Array(sha1Size)) { - this.digest = digest; + constructor(refStr: string = emtpyRefStr) { + this._refStr = refStr; + } + + get ref(): Ref { + return this; + } + + get digest(): Uint8Array { + return hexToUint8(this._refStr.substring(5)); } isEmpty(): boolean { - for (let i = 0; i < sha1Size; i++) { - if (this.digest[i] !== 0) { - return false; - } - } - - return true; + return this._refStr === emtpyRefStr; } - equals(other: Ref): boolean { - for (let i = 0; i < sha1Size; i++) { - if (this.digest[i] !== other.digest[i]) { - return false; - } - } - - return true; + equals(other: Value): boolean { + invariant(other instanceof Ref); + return this._refStr === other._refStr; } - compare(other: Ref): number { - for (let i = 0; i < sha1Size; i++) { - if (this.digest[i] < other.digest[i]) { - return -1; - } else if (this.digest[i] > other.digest[i]) { - return 1; - } - } - - return 0; + less(other: Value): boolean { + invariant(other instanceof Ref); + return this._refStr < other._refStr; } toString(): string { - return 'sha1-' + uint8ArrayToHex(this.digest); + return this._refStr; } static parse(s: string): Ref { @@ -79,11 +75,14 @@ export default class Ref { throw Error('Could not parse ref: ' + s); } - return new Ref(hexToUint8(m[1])); + return new Ref(m[1]); + } + + static fromDigest(digest: Uint8Array = new Uint8Array(sha1Size)) { + return new Ref(sha1Prefix + uint8ArrayToHex(digest)); } static fromData(data: Uint8Array): Ref { - let digest = r.rawDigest(data); - return new Ref(new Uint8Array(digest.buffer)); + return new Ref(sha1Prefix + r.digest(data)); } } diff --git a/js/src/ref_test.js b/js/src/ref_test.js index 2670ae93c3..a3194c8ce3 100644 --- a/js/src/ref_test.js +++ b/js/src/ref_test.js @@ -56,11 +56,11 @@ suite('Ref', () => { test('isEmpty', () => { let digest = new Uint8Array(20); - let r = new Ref(digest); + let r = Ref.fromDigest(digest); assert.isTrue(r.isEmpty()); digest[0] = 10; - r = new Ref(digest); + r = Ref.fromDigest(digest); assert.isFalse(r.isEmpty()); r = new Ref(); diff --git a/js/src/sequence.js b/js/src/sequence.js index 43432f0a7c..545a1261f8 100644 --- a/js/src/sequence.js +++ b/js/src/sequence.js @@ -3,32 +3,32 @@ import type {ChunkStore} from './chunk_store.js'; import {invariant, notNull} from './assert.js'; import {Type} from './type.js'; -import {Value} from './value.js'; +import {ValueBase} from './value.js'; -export class Sequence extends Value { - cs: ChunkStore; +export class Sequence extends ValueBase { items: Array; isMeta: boolean; - constructor(cs: ChunkStore, type: Type, items: Array) { + constructor(type: Type, items: Array) { super(type); - this.cs = cs; this.items = items; this.isMeta = false; } - getChildSequence(idx: number): Promise { // eslint-disable-line no-unused-vars + getChildSequence(cs: ChunkStore, idx: number): Promise { // eslint-disable-line no-unused-vars return Promise.resolve(null); } } export class SequenceCursor { + cs: ChunkStore; parent: ?SequenceCursor; sequence: S; idx: number; - constructor(parent: ?SequenceCursor, sequence: S, idx: number) { + constructor(cs: ChunkStore, parent: ?SequenceCursor, sequence: S, idx: number) { + this.cs = cs; this.parent = parent; this.sequence = sequence; this.idx = idx; @@ -48,7 +48,7 @@ export class SequenceCursor { } getChildSequence(): Promise { - return this.sequence.getChildSequence(this.idx); + return this.sequence.getChildSequence(this.cs, this.idx); } getCurrent(): T { @@ -125,10 +125,6 @@ export class SequenceCursor { return false; } - copy(): SequenceCursor { - return new SequenceCursor(this.parent ? this.parent.copy() : null, this.sequence, this.idx); - } - async iter(cb: (v: T, i: number) => boolean): Promise { let idx = 0; while (this.valid) { diff --git a/js/src/set.js b/js/src/set.js index d585ce7a11..293401b4b4 100644 --- a/js/src/set.js +++ b/js/src/set.js @@ -1,22 +1,24 @@ // @flow -import type {ChunkStore} from './chunk_store.js'; import type {valueOrPrimitive} from './value.js'; // eslint-disable-line no-unused-vars +import {Collection} from './collection.js'; import {equals, less} from './value.js'; import {invariant} from './assert.js'; -import {Kind} from './noms_kind.js'; import {OrderedSequence} from './ordered_sequence.js'; -import {registerMetaValue, MetaTuple} from './meta_sequence.js'; -import {Type} from './type.js'; -export class NomsSet extends OrderedSequence { +export class NomsSet extends Collection { + async has(key: T): Promise { + let cursor = await this.sequence.newCursorAt(this.cs, key); + return cursor.valid && equals(cursor.getCurrentKey(), key); + } + async first(): Promise { - let cursor = await this.newCursorAt(null); + let cursor = await this.sequence.newCursorAt(this.cs, null); return cursor.valid ? cursor.getCurrent() : null; } async forEach(cb: (v: T) => void): Promise { - let cursor = await this.newCursorAt(null); + let cursor = await this.sequence.newCursorAt(this.cs, null); return cursor.iter(v => { cb(v); return false; @@ -24,10 +26,14 @@ export class NomsSet extends OrderedSequence { } get size(): number { - return this.items.length; + if (this.sequence instanceof SetLeafSequence) { + return this.sequence.items.length; + } + + throw new Error('not implemented'); } - async intersect(...sets: Array): Promise { + async intersect(...sets: Array>): Promise> { if (sets.length === 0) { return this; } @@ -37,16 +43,17 @@ export class NomsSet extends OrderedSequence { invariant(sets[i].type.equals(this.type)); } - let cursor = await this.newCursorAt(null); + let cursor = await this.sequence.newCursorAt(this.cs, null); if (!cursor.valid) { return this; } - let values: Array = []; + let values: Array = []; for (let i = 0; cursor.valid && i < sets.length; i++) { let first = cursor.getCurrent(); - let next = await sets[i].newCursorAt(first); + let set: NomsSet = sets[i]; + let next = await set.sequence.newCursorAt(set.cs, first); if (!next.valid) { break; } @@ -61,40 +68,16 @@ export class NomsSet extends OrderedSequence { } // TODO: Chunk the resulting set. - return new SetLeaf(this.cs, this.type, values); + return new NomsSet(this.cs, this.type, new SetLeafSequence(this.type, values)); } } -export class SetLeaf extends NomsSet { +export class SetLeafSequence extends OrderedSequence { getKey(idx: number): K { return this.items[idx]; } } -export class CompoundSet extends NomsSet> { - constructor(cs: ChunkStore, type: Type, items: Array) { - super(cs, type, items); - this.isMeta = true; - } - - getKey(idx: number): K { - return this.items[idx].value; - } - - async getChildSequence(idx: number): Promise { - let mt = this.items[idx]; - let ms = await mt.readValue(this.cs); - invariant(ms instanceof NomsSet); - return ms; - } - - get size(): number { - throw new Error('not implemented'); - } -} - -registerMetaValue(Kind.Set, (cs, type, tuples) => new CompoundSet(cs, type, tuples)); - type OrderedCursor = { valid: boolean; getCurrent(): K; diff --git a/js/src/set_test.js b/js/src/set_test.js index 446cb2704c..506bbb494f 100644 --- a/js/src/set_test.js +++ b/js/src/set_test.js @@ -5,18 +5,21 @@ import {suite} from 'mocha'; import MemoryStore from './memory_store.js'; import test from './async_test.js'; -import {CompoundSet, NomsSet, SetLeaf} from './set.js'; -import {notNull} from './assert.js'; +import type {ChunkStore} from './chunk_store.js'; +import {invariant} from './assert.js'; import {Kind} from './noms_kind.js'; import {makeCompoundType, makePrimitiveType} from './type.js'; -import {MetaTuple} from './meta_sequence.js'; +import {MetaTuple, OrderedMetaSequence} from './meta_sequence.js'; +import {NomsSet, SetLeafSequence} from './set.js'; +import {notNull} from './assert.js'; +import {OrderedSequence} from './ordered_sequence.js'; import {writeValue} from './encode.js'; suite('SetLeaf', () => { test('first/has', async () => { let ms = new MemoryStore(); let tr = makeCompoundType(Kind.Set, makePrimitiveType(Kind.String)); - let s = new SetLeaf(ms, tr, ['a', 'k']); + let s = new NomsSet(ms, tr, new SetLeafSequence(tr, ['a', 'k'])); assert.strictEqual('a', await s.first()); @@ -29,7 +32,7 @@ suite('SetLeaf', () => { test('forEach', async () => { let ms = new MemoryStore(); let tr = makeCompoundType(Kind.Set, makePrimitiveType(Kind.String)); - let m = new SetLeaf(ms, tr, ['a', 'b']); + let m = new NomsSet(ms, tr, new SetLeafSequence(tr, ['a', 'b'])); let values = []; await m.forEach((k) => { values.push(k); }); @@ -38,16 +41,14 @@ suite('SetLeaf', () => { }); suite('CompoundSet', () => { - function build(values: Array): NomsSet { - let ms = new MemoryStore(); - + function build(cs: ChunkStore, values: Array): NomsSet { let tr = makeCompoundType(Kind.Set, makePrimitiveType(Kind.String)); assert.isTrue(values.length > 1 && Math.log2(values.length) % 1 === 0); let tuples = []; for (let i = 0; i < values.length; i += 2) { - let l = new SetLeaf(ms, tr, [values[i], values[i + 1]]); - let r = writeValue(l, tr, ms); + let l = new NomsSet(cs, tr, new SetLeafSequence(tr, [values[i], values[i + 1]])); + let r = writeValue(l, tr, cs); tuples.push(new MetaTuple(r, values[i + 1])); } @@ -55,8 +56,8 @@ suite('CompoundSet', () => { while (tuples.length > 1) { let next = []; for (let i = 0; i < tuples.length; i += 2) { - last = new CompoundSet(ms, tr, [tuples[i], tuples[i + 1]]); - let r = writeValue(last, tr, ms); + last = new NomsSet(cs, tr, new OrderedMetaSequence(tr, [tuples[i], tuples[i + 1]])); + let r = writeValue(last, tr, cs); next.push(new MetaTuple(r, tuples[i + 1].value)); } @@ -67,7 +68,8 @@ suite('CompoundSet', () => { } test('first/has', async () => { - let c = build(['a', 'b', 'e', 'f', 'h', 'i', 'm', 'n']); + let ms = new MemoryStore(); + let c = build(ms, ['a', 'b', 'e', 'f', 'h', 'i', 'm', 'n']); assert.strictEqual('a', await c.first()); assert.isTrue(await c.has('a')); assert.isTrue(await c.has('b')); @@ -86,7 +88,8 @@ suite('CompoundSet', () => { }); test('forEach', async () => { - let c = build(['a', 'b', 'e', 'f', 'h', 'i', 'm', 'n']); + let ms = new MemoryStore(); + let c = build(ms, ['a', 'b', 'e', 'f', 'h', 'i', 'm', 'n']); let values = []; await c.forEach((k) => { values.push(k); }); assert.deepEqual(['a', 'b', 'e', 'f', 'h', 'i', 'm', 'n'], values); @@ -104,9 +107,12 @@ suite('CompoundSet', () => { } test('advanceTo', async () => { - let c = build(['a', 'b', 'e', 'f', 'h', 'i', 'm', 'n']); + let ms = new MemoryStore(); - let cursor = await c.newCursorAt(null); + let c = build(ms, ['a', 'b', 'e', 'f', 'h', 'i', 'm', 'n']); + + invariant(c.sequence instanceof OrderedSequence); + let cursor = await c.sequence.newCursorAt(c.cs, null); assert.ok(cursor); assert.strictEqual('a', cursor.getCurrent()); @@ -119,10 +125,12 @@ suite('CompoundSet', () => { assert.isFalse(await cursor.advanceTo('z')); // not found assert.isFalse(cursor.valid); - cursor = await c.newCursorAt('x'); // not found + invariant(c.sequence instanceof OrderedSequence); + cursor = await c.sequence.newCursorAt(ms, 'x'); // not found assert.isFalse(cursor.valid); - cursor = await c.newCursorAt('e'); + invariant(c.sequence instanceof OrderedSequence); + cursor = await c.sequence.newCursorAt(ms, 'e'); assert.ok(cursor); assert.strictEqual('e', cursor.getCurrent()); @@ -141,10 +149,12 @@ suite('CompoundSet', () => { }); async function testIntersect(expect: Array, seqs: Array>) { - let first = build(seqs[0]); + let ms = new MemoryStore(); + + let first = build(ms, seqs[0]); let sets:Array = []; for (let i = 1; i < seqs.length; i++) { - sets.push(build(seqs[i])); + sets.push(build(ms, seqs[i])); } let result = await first.intersect(...sets); diff --git a/js/src/struct.js b/js/src/struct.js index 346c61c5b6..d181a5f471 100644 --- a/js/src/struct.js +++ b/js/src/struct.js @@ -2,11 +2,11 @@ import {Field, StructDesc, Type} from './type.js'; import {invariant, notNull} from './assert.js'; -import {Value} from './value.js'; +import {ValueBase} from './value.js'; type StructData = {[key: string]: any}; -export default class Struct extends Value { +export default class Struct extends ValueBase { desc: StructDesc; unionField: ?Field; diff --git a/js/src/value.js b/js/src/value.js index 41ab50d308..6b3c78b1a6 100644 --- a/js/src/value.js +++ b/js/src/value.js @@ -6,7 +6,12 @@ import {ensureRef} from './get_ref.js'; import {invariant} from './assert.js'; import {Type} from './type.js'; -export class Value { +export type Value = { + ref: Ref; + equals(other: Value): boolean; +} + +export class ValueBase { type: Type; _ref: ?Ref; @@ -31,16 +36,16 @@ export function less(v1: any, v2: any): boolean { if (typeof v1 === 'object') { invariant(typeof v2 === 'object'); - if (v1 instanceof Value) { + if (v1 instanceof ValueBase) { v1 = v1.ref; } - if (v2 instanceof Value) { + if (v2 instanceof ValueBase) { v2 = v2.ref; } invariant(v1 instanceof Ref); invariant(v2 instanceof Ref); - return v1.compare(v2) < 0; + return v1.less(v2); } if (typeof v1 === 'string') {