Skip to content

Commit aa22a07

Browse files
committed
now we save bandwidth by sending base64-ized points to the client.
1 parent cdfb22c commit aa22a07

6 files changed

Lines changed: 129 additions & 80 deletions

File tree

bookmarklet.d/30-shapes.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,6 @@ function Shape(thickness, r,g,b,a, points, id) {
9191
this.p = points||[];
9292
this.id = (typeof id == 'undefined')? null : id;
9393
}
94-
Shape.fromJSON = function(shape) {
95-
// Convert an array of shapes to real shapes.
96-
return new Shape(shape.t, shape.r,shape.g,shape.b,shape.a, shape.p, shape.id);
97-
};
9894
Shape.prototype.appendPoint = function(point) {
9995
// Append a point to this shape.
10096
this.p.push(
@@ -245,3 +241,7 @@ Shape.prototype.serializePoints = function(){
245241
}
246242
return result;
247243
};
244+
Shape.fromJSON = function(shape) {
245+
// Convert an array of shapes to real shapes.
246+
return new Shape(shape.t, shape.r,shape.g,shape.b,shape.a, b64ToPoints(shape.p), shape.id);
247+
};

models/page.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
// change state happen only one at a time.
1212
//
1313
var History = require('./history').History,
14-
AsyncLock = require('./async_lock').AsyncLock;
14+
AsyncLock = require('./async_lock').AsyncLock,
15+
Shape = require('./shape').Shape;
1516

1617
function Page(ks, key, emptyCbTimeout) {
1718
// Page keeps info on the current page; the keystore and key index us, and
@@ -39,13 +40,13 @@ Page.prototype.get = function(cb) {
3940
if (diskInfo) {
4041
// we got info from disk
4142
self.info = {
42-
shapes: diskInfo.shapes.map(function(shape, index){
43+
shapes: diskInfo.shapes.map(function(jsonshape, index){
4344
// Assign each shape an ID if it does not already have one.
44-
if (typeof shape.id == 'undefined' || shape.id === null) {
45-
shape.id = index;
45+
if (typeof jsonshape.id == 'undefined' || jsonshape.id === null) {
46+
jsonshape.id = index;
4647
diskInfo.nextId = (diskInfo.nextId||index)+1;
4748
}
48-
return shape;
49+
return new Shape.fromJSON(jsonshape);
4950
}),
5051
nextId: diskInfo.nextId
5152
};
@@ -67,7 +68,9 @@ Page.prototype.JSONize = function(cb) {
6768
var self = this;
6869
this.get(function(pageInfo) {
6970
cb({
70-
shapes: pageInfo.shapes,
71+
shapes: pageInfo.shapes.map(function(shape){
72+
return shape.toJSONWithSerializedPoints();
73+
}),
7174
nextUpdate: self.history.time()
7275
});
7376
});
@@ -81,7 +84,9 @@ Page.prototype.sync = function(cb) {
8184
self.get(function(pageInfo) {
8285
// todo: do we really need that get? /really/ ?
8386
self.ks.set(self.key, {
84-
shapes: pageInfo.shapes,
87+
shapes: pageInfo.shapes.map(function(shape){
88+
return shape.toJSON();
89+
}),
8590
nextId: pageInfo.nextId
8691
}, cb);
8792
});
@@ -98,10 +103,10 @@ Page.prototype.deleteShapeFromPage = function(shapeId, cb) {
98103
// find shape (pointwise comparison)
99104
var shapes = pageInfo.shapes,
100105
foundShape = null;
101-
pageInfo.shapes.forEach(function(shape, id) {
106+
pageInfo.shapes.forEach(function(shape, index) {
102107
// todo! keep shapes inside a dictionary
103108
if (shape.id == shapeId) {
104-
foundShape = id;
109+
foundShape = index;
105110
}
106111
});
107112
if (foundShape === null) {
@@ -153,7 +158,7 @@ Page.prototype.addShapeToPage = function(shape, cb) {
153158
} else {
154159
cb(true);
155160
self.history.add(
156-
{add_shape: shape}
161+
{add_shape: shape.toJSONWithSerializedPoints()}
157162
);
158163
unlock();
159164
}

models/pagestore.js

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -49,67 +49,4 @@ Pagestore.prototype.streamPageUpdates = function(key, since, cb) {
4949
return this.get(key).streamPageUpdates(since, cb);
5050
};
5151

52-
// we serialize our points to something similar to base64
53-
// major changes: we use _ instead of / and - instead of + as the last
54-
// characters (it's being sent in a query string)
55-
var b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
56-
function numToB64(num) {
57-
// return a 3-digit base64 number that represents num
58-
num = num + 131072; // this bias is 64**3 / 2 and allows us to represent negative numbers
59-
var result = "";
60-
for (var i = 0; i < 3; i++) {
61-
result = b64[ num%64 ] + result;
62-
num = parseInt(num / 64,10);
63-
}
64-
return result;
65-
}
66-
function b64ToNum(b) {
67-
var result = 0;
68-
for (var i=0,l=b.length; i<l; i++) {
69-
var chr = b64.indexOf(b[i]);
70-
if (chr == -1) {
71-
throw new Error("contained a char that wasn't our base64");
72-
}
73-
result *= 64;
74-
result += chr;
75-
}
76-
return result - 131072;
77-
}
78-
function b64ToPoints(b) {
79-
if ((b.length % 6) !== 0) {
80-
throw new Error("invalid point length");
81-
}
82-
var points = [];
83-
for (var i=0,l=b.length; i<l; i+=6) {
84-
var x = b64ToNum(b.substring(i, i+3)),
85-
y = b64ToNum(b.substring(i+3, i+6));
86-
points.push( [x,y] );
87-
}
88-
return points;
89-
}
90-
91-
Pagestore.prototype.verifyShape = function(points, t, r,g,b,a) {
92-
// Returns a new shape with given points (p), thickness t, color rgba.
93-
// TODO: this REALLY REALLY REALLY doesn't belong here! booooo!
94-
// at least make this a static method for heavens sake!
95-
try {
96-
t = t? parseFloat(t):3;
97-
r = r? parseFloat(r):0;
98-
g = g? parseFloat(g):0;
99-
b = b? parseFloat(b):0;
100-
a = a? parseFloat(a):1;
101-
// now verify points
102-
points = b64ToPoints(points);
103-
if (points.length===0) {
104-
console.log(new Error("No points").stack);
105-
return false;
106-
} else {
107-
return {t: t, p: points, r:r,g:g,b:b,a:a};
108-
}
109-
} catch(e) {
110-
console.log(e.stack); // wth?
111-
return false;
112-
}
113-
};
114-
11552
exports.Pagestore = Pagestore;

models/shape.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// shape.js
2+
//
3+
// much-needed abstraction for a certain shape
4+
//
5+
function Shape(thickness, r,g,b,a, points, id) {
6+
// Build a new shape
7+
this.t = thickness;
8+
this.r = r;
9+
this.g = g;
10+
this.b = b;
11+
this.a = a;
12+
13+
this.p = points;
14+
15+
this.id = id; // I REALLY don't like putting this here.
16+
}
17+
// we serialize our points to something similar to base64
18+
// major changes: we use _ instead of / and - instead of + as the last
19+
// characters (it's being sent in a query string)
20+
var b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
21+
function numToB64(num) {
22+
// return a 3-digit base64 number that represents num
23+
num = num + 131072; // this bias is 64**3 / 2 and allows us to represent negative numbers
24+
var result = "";
25+
for (var i = 0; i < 3; i++) {
26+
result = b64[ num%64 ] + result;
27+
num = parseInt(num / 64,10);
28+
}
29+
return result;
30+
}
31+
function b64ToNum(b) {
32+
var result = 0;
33+
for (var i=0,l=b.length; i<l; i++) {
34+
var chr = b64.indexOf(b[i]);
35+
if (chr == -1) {
36+
throw new Error("contained a char that wasn't our base64");
37+
}
38+
result *= 64;
39+
result += chr;
40+
}
41+
return result - 131072;
42+
}
43+
function b64ToPoints(b) {
44+
if ((b.length % 6) !== 0) {
45+
throw new Error("invalid point length");
46+
}
47+
var points = [];
48+
for (var i=0,l=b.length; i<l; i+=6) {
49+
var x = b64ToNum(b.substring(i, i+3)),
50+
y = b64ToNum(b.substring(i+3, i+6));
51+
points.push( [x,y] );
52+
}
53+
return points;
54+
}
55+
Shape.fromParams = function(points, t, r,g,b,a){
56+
// Returns a new shape with given points (p), thickness t, color rgba.
57+
t = t? parseFloat(t):3;
58+
r = r? parseFloat(r):0;
59+
g = g? parseFloat(g):0;
60+
b = b? parseFloat(b):0;
61+
a = a? parseFloat(a):1;
62+
// now verify points
63+
points = b64ToPoints(points);
64+
if (points.length===0) {
65+
throw new Error("No points");
66+
} else {
67+
return new Shape(t, r,g,b,a, points, 0);
68+
}
69+
};
70+
Shape.fromJSON = function(data){
71+
// this json is assumed to contain normal points (e.g. it's saved on disk)
72+
return new Shape(data.t, data.r,data.g,data.b,data.a, data.p, data.id||0);
73+
};
74+
75+
Shape.prototype.serializePoints = function(){
76+
var result = "";
77+
for (var i=0,l=this.p.length; i<l; i++) {
78+
result += numToB64(this.p[i][0])+numToB64(this.p[i][1]);
79+
}
80+
return result;
81+
};
82+
83+
Shape.prototype.toJSON = function(){
84+
return {
85+
t: this.t,
86+
r: this.r,
87+
g: this.g,
88+
b: this.b,
89+
a: this.a,
90+
p: this.p,
91+
id: this.id
92+
};
93+
};
94+
Shape.prototype.toJSONWithSerializedPoints = function(){
95+
return {
96+
t: this.t,
97+
r: this.r,
98+
g: this.g,
99+
b: this.b,
100+
a: this.a,
101+
p: this.serializePoints(),
102+
id: this.id
103+
};
104+
};
105+
106+
exports.Shape = Shape;

site.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
var http = require('http'),
1212
url = require('url'),
1313
Pagestore = require('./models/pagestore').Pagestore,
14+
Shape = require('./models/shape').Shape,
1415
bmr = require('./views/bookmarklet_renderer'),
1516
staticfiles = require('./views/static'),
1617
view = require('./views/view_helpers'),
@@ -47,7 +48,7 @@ exports.makeGogglesServer = function(conf) {
4748
'page': switchboard.makeDispatchQueryOverloader(
4849
['add', 'page','t','r','g','b','a','p'],
4950
function(req,res, add,page,t,r,g,b,a,p){
50-
var shape = ps.verifyShape(p, t, r, g, b, a);
51+
var shape = Shape.fromParams(p, t, r, g, b, a);
5152
if (shape) {
5253
console.log((req.headers['x-forwarded-for']||req.connection.remoteAddress)+" +++ "+page);
5354
return ps.addShapeToPage(page, shape, render);

test/protocol-test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ vows.describe('Client-server protocol').addBatch({
6868
'the page should have the shape we added.': herr(function(page){
6969
assert.deepEqual(page.shapes, [
7070
{t: 15, r: 0, g: 25, b: 250, a: 0,
71-
p: [[12,35],[35,99],[0,-235]], id: 0}]);
71+
p: "gAMgAjgAjgBjgAAf8V", id: 0}]);
7272
}),
7373
'the page should advance its history': herr(function(page){
7474
assert.equal(page.nextUpdate, 1);
@@ -157,7 +157,7 @@ vows.describe('Client-server protocol').addBatch({
157157
'they should be reported': herr(function(data){
158158
assert.deepEqual(data, [
159159
{add_shape: { t: 1, r: 0, g: 25, b: 250, a: 0,
160-
p: [[1,2],[3,4],[5,6]],id:0}}
160+
p: "gABgACgADgAEgAFgAG",id:0}}
161161
]);
162162
}),
163163
'and when we delete shapes': {

0 commit comments

Comments
 (0)