mirror of
https://github.com/dolthub/dolt.git
synced 2026-05-11 19:11:10 -05:00
Beginnings of new js sdk
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
[ignore]
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
|
||||
[options]
|
||||
unsafe.enable_getters_and_setters=true
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
dist
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "newjs",
|
||||
"main": "dist/noms.js",
|
||||
"dependencies": {
|
||||
"rusha": "^0.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel": "^5.6.23",
|
||||
"chai": "^3.2.0",
|
||||
"mocha": "^2.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "babel -w src/ -d dist/",
|
||||
"build": "babel src/ -d dist/",
|
||||
"test": "babel src/ -d dist/; flow; mocha --ui tdd dist/"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Ref = require('./ref.js');
|
||||
|
||||
class Chunk {
|
||||
ref: Ref;
|
||||
data: string;
|
||||
|
||||
constructor(data: string = '', ref: ?Ref) {
|
||||
this.data = data;
|
||||
this.ref = ref ? ref : Ref.fromData(data);
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this.data.length === 0;
|
||||
}
|
||||
|
||||
static emptyChunk: Chunk;
|
||||
}
|
||||
|
||||
Chunk.emptyChunk = new Chunk();
|
||||
|
||||
module.exports = Chunk;
|
||||
@@ -0,0 +1,35 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {suite, test} = require('mocha');
|
||||
const {assert} = require('chai');
|
||||
const Chunk = require('./chunk.js');
|
||||
const Ref = require('./ref.js');
|
||||
|
||||
suite('Chunk', () => {
|
||||
test('construct', () => {
|
||||
let c = new Chunk('abc');
|
||||
assert.strictEqual(c.data, 'abc');
|
||||
assert.isTrue(c.ref.equals(Ref.parse('sha1-a9993e364706816aba3e25717850c26c9cd0d89d')));
|
||||
assert.isFalse(c.isEmpty());
|
||||
});
|
||||
|
||||
test('construct with ref', () => {
|
||||
let ref = Ref.parse('sha1-0000000000000000000000000000000000000001');
|
||||
let c = new Chunk('abc', ref);
|
||||
assert.strictEqual(c.data, 'abc');
|
||||
assert.isTrue(c.ref.equals(Ref.parse('sha1-0000000000000000000000000000000000000001')))
|
||||
assert.isFalse(c.isEmpty());
|
||||
});
|
||||
|
||||
test('isEmpty', () => {
|
||||
function assertChunkIsEmpty(c: Chunk) {
|
||||
assert.strictEqual(c.data.length, 0);
|
||||
assert.isTrue(c.isEmpty());
|
||||
}
|
||||
|
||||
assertChunkIsEmpty(new Chunk());
|
||||
assertChunkIsEmpty(new Chunk(''));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Ref = require('./ref.js');
|
||||
const Chunk = require('./chunk.js');
|
||||
|
||||
class MemoryStore {
|
||||
_data: { [key: string]: Chunk };
|
||||
_root: Ref;
|
||||
|
||||
constructor() {
|
||||
this._data = Object.create(null);
|
||||
this._root = new Ref();
|
||||
}
|
||||
|
||||
get root(): Ref {
|
||||
return this._root;
|
||||
}
|
||||
|
||||
updateRoot(current: Ref, last: Ref): boolean {
|
||||
if (!this._root.equals(last)) {
|
||||
return false
|
||||
}
|
||||
|
||||
this._root = current;
|
||||
return true;
|
||||
}
|
||||
|
||||
get(ref: Ref): Chunk {
|
||||
var c = this._data[ref.toString()];
|
||||
if (c == null) {
|
||||
c = Chunk.emptyChunk;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
has(ref: Ref): boolean {
|
||||
return this._data[ref.toString()] == null;
|
||||
}
|
||||
|
||||
put(c: Chunk) {
|
||||
this._data[c.ref.toString()] = c;
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return Object.keys(this._data).length;
|
||||
}
|
||||
|
||||
close() {}
|
||||
}
|
||||
|
||||
module.exports = MemoryStore;
|
||||
@@ -0,0 +1,60 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {suite, test} = require('mocha');
|
||||
const {assert} = require('chai');
|
||||
const Chunk = require('./chunk.js');
|
||||
const Ref = require('./ref.js');
|
||||
const MemoryStore = require('./memory_store.js');
|
||||
|
||||
suite('MemoryStore', () => {
|
||||
function assertInputInStore(input: string, ref: Ref, ms: MemoryStore) {
|
||||
let chunk = ms.get(ref);
|
||||
assert.isFalse(chunk.isEmpty());
|
||||
assert.strictEqual(input, chunk.data);
|
||||
}
|
||||
|
||||
test('put', () => {
|
||||
let ms = new MemoryStore();
|
||||
let input = 'abc';
|
||||
let c = new Chunk(input);
|
||||
ms.put(c);
|
||||
|
||||
// See http://www.di-mgt.com.au/sha_testvectors.html
|
||||
assert.strictEqual('sha1-a9993e364706816aba3e25717850c26c9cd0d89d', c.ref.toString());
|
||||
|
||||
ms.updateRoot(c.ref, ms.root);
|
||||
|
||||
assertInputInStore(input, c.ref, ms);
|
||||
|
||||
// Re-writing the same data should be idempotent and should not result in a second put
|
||||
c = new Chunk(input);
|
||||
ms.put(c);
|
||||
assertInputInStore(input, c.ref, ms);
|
||||
});
|
||||
|
||||
test('updateRoot', () => {
|
||||
let ms = new MemoryStore();
|
||||
let oldRoot = ms.root;
|
||||
assert.isTrue(oldRoot.isEmpty());
|
||||
|
||||
let bogusRoot = Ref.parse('sha1-81c870618113ba29b6f2b396ea3a69c6f1d626c5'); // sha1("Bogus, Dude")
|
||||
let newRoot = Ref.parse('sha1-907d14fb3af2b0d4f18c2d46abe8aedce17367bd'); // sha1("Hello, World")
|
||||
|
||||
// Try to update root with bogus oldRoot
|
||||
let result = ms.updateRoot(newRoot, bogusRoot);
|
||||
assert.isFalse(result);
|
||||
|
||||
// Now do a valid root update
|
||||
result = ms.updateRoot(newRoot, oldRoot);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
|
||||
test('get non-existing', () => {
|
||||
let ms = new MemoryStore();
|
||||
let ref = Ref.parse('sha1-1111111111111111111111111111111111111111');
|
||||
let c = ms.get(ref);
|
||||
assert.isTrue(c.isEmpty());
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Rusha = require('rusha');
|
||||
|
||||
const r = new Rusha();
|
||||
const sha1Size = 20;
|
||||
const pattern = /^sha1-([0-9a-f]{40})$/;
|
||||
|
||||
function uint8ArrayToHex(a: Uint8Array): string {
|
||||
let hex = '';
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
let v = a[i].toString(16);
|
||||
if (v.length == 1) {
|
||||
hex += '0' + v;
|
||||
} else {
|
||||
hex += v;
|
||||
}
|
||||
}
|
||||
|
||||
return hex;
|
||||
}
|
||||
|
||||
function hexToUint8(s: string): Uint8Array {
|
||||
let digest = new Uint8Array(sha1Size);
|
||||
for (let i = 0; i < sha1Size; i++) {
|
||||
let ch = s.substring(i*2, i*2 + 2);
|
||||
digest[i] = parseInt(ch, 16)
|
||||
}
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
class Ref {
|
||||
digest: Uint8Array;
|
||||
|
||||
constructor(digest: Uint8Array = new Uint8Array(sha1Size)) {
|
||||
this.digest = digest;
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
for (let i = 0; i < sha1Size; i++) {
|
||||
if (this.digest[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
equals(other: Ref): boolean {
|
||||
for (let i = 0; i < sha1Size; i++) {
|
||||
if (this.digest[i] != other.digest[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return 'sha1-' + uint8ArrayToHex(this.digest);
|
||||
}
|
||||
|
||||
static parse(s: string): Ref {
|
||||
let m = s.match(pattern);
|
||||
if (m === null) {
|
||||
throw Error('Could not parse ref: ' + s);
|
||||
}
|
||||
|
||||
return new Ref(hexToUint8(m[1]));
|
||||
}
|
||||
|
||||
static fromData(data: string): Ref {
|
||||
let digest = r.rawDigest(data);
|
||||
return new Ref(new Uint8Array(digest.buffer));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Ref;
|
||||
@@ -0,0 +1,70 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {suite, test} = require('mocha');
|
||||
const {assert} = require('chai');
|
||||
const Ref = require('./ref.js');
|
||||
|
||||
suite('Ref', () => {
|
||||
test('parse', () => {
|
||||
function assertParseError(s) {
|
||||
assert.throws(() => {
|
||||
Ref.parse(s);
|
||||
});
|
||||
}
|
||||
|
||||
assertParseError('foo');
|
||||
assertParseError('sha1');
|
||||
assertParseError('sha1-0');
|
||||
|
||||
// too many digits
|
||||
assertParseError('sha1-00000000000000000000000000000000000000000');
|
||||
|
||||
// 'g' not valid hex
|
||||
assertParseError('sha1- 000000000000000000000000000000000000000g');
|
||||
|
||||
// sha2 not supported
|
||||
assertParseError('sha2-0000000000000000000000000000000000000000');
|
||||
|
||||
let r = Ref.parse('sha1-0000000000000000000000000000000000000000');
|
||||
assert.isNotNull(r);
|
||||
});
|
||||
|
||||
test('equals', () => {
|
||||
let r0 = Ref.parse('sha1-0000000000000000000000000000000000000000');
|
||||
let r01 = Ref.parse('sha1-0000000000000000000000000000000000000000');
|
||||
let r1 = Ref.parse('sha1-0000000000000000000000000000000000000001');
|
||||
|
||||
assert.isTrue(r0.equals(r01));
|
||||
assert.isTrue(r01.equals(r0));
|
||||
assert.isFalse(r0.equals(r1));
|
||||
assert.isFalse(r1.equals(r0));
|
||||
});
|
||||
|
||||
test('toString', () => {
|
||||
let s = 'sha1-0123456789abcdef0123456789abcdef01234567';
|
||||
let r = Ref.parse(s);
|
||||
assert.strictEqual(s, r.toString());
|
||||
});
|
||||
|
||||
test('fromData', () => {
|
||||
let r = Ref.fromData('abc');
|
||||
|
||||
assert.strictEqual('sha1-a9993e364706816aba3e25717850c26c9cd0d89d', r.toString());
|
||||
});
|
||||
|
||||
test('isEmpty', () => {
|
||||
let digest = new Uint8Array(20);
|
||||
let r = new Ref(digest);
|
||||
assert.isTrue(r.isEmpty());
|
||||
|
||||
digest[0] = 10;
|
||||
r = new Ref(digest);
|
||||
assert.isFalse(r.isEmpty());
|
||||
|
||||
r = new Ref();
|
||||
assert.isTrue(r.isEmpty());
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user