Permalink
Browse files

Initial implementation.

  • Loading branch information...
0 parents commit 82a52b5c33d847514b4dab1a798ec821ff50623f @pgriess committed Jul 14, 2010
Showing with 732 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +38 −0 README.md
  3. +154 −0 lib/ns.js
  4. +425 −0 test/async_testing.js
  5. +114 −0 test/test.js
@@ -0,0 +1 @@
+README.html
@@ -0,0 +1,38 @@
+A braindead module for reading and writing
+[netstrings](http://cr.yp.to/proto/netstrings.txt).
+
+## API
+ nsWrite(pay, payStart = 0, payEnd = pay.length, buf = undefined, bufOff = 0)
+
+Write the payload `pay ` out in netstring format, returning a string. The
+`payStart` and `payEnd` parameters allow specifying a range for the payload
+and default to the entire object. Like `Buffer.slice()`, this `payStart` is
+inclusive and `payEnd` is exclusive. The `pay` parameter can be either a
+string or a `Buffer` object.
+
+If the `buf` parameter is specified, the netstring is written to this buffer
+rather than returned as a string. The `bufOff` parameter allows specifying
+the offset into the buffer at which to begin writing.
+
+ nsPayload(buf, off = 0)
+
+Get the payload of the netstring pointed to by the given `buf` object at
+offset `off`. Despite its name, `buf`, can be either a string or a `Buffer`.
+The returned value will be of the same type as the `buf` parameter or a
+negative integer value in following the error taxonomy of
+`nsPayloadLength()`.
+
+ nsPayloadLength(buf, off = 0)
+
+Get the length of the payload pointed to by the given `buf` object at offset
+`off`. Despite its name, `buf`, can be either a string or a `Buffer`. The
+length returned is only that of the payload; it does not include the header
+or footer. The returned value will be -1 if the buffer does not include
+enough data to make a complete length calculation.
+
+ nsLength(buf, off = 0)
+
+Get the length of the netstring pointed to by the given `buf` object at
+offset `off`. Despite its name, `buf`, can be either a string or a `Buffer`.
+The length returned includes the length of the header and footer in addition
+to the payload. Negative values follow the taxonomy from `nsPayloadLength()`.
@@ -0,0 +1,154 @@
+// An implementation of the http://cr.yp.to/proto/netstrings.txt format.
+
+var assert = require('assert');
+var sys = require('sys');
+
+// Get the length of the netstring payload (i.e. excluding header and footer)
+// pointed to by Buffer or String 'buf'. Returns -1 if the buffer is
+// incomplete (note that this happens even if we're only missing the trailing
+// ',').
+var nsPayloadLength = function(buf, off) {
+ off = off || 0;
+
+ var charCode = (typeof buf === 'string') ?
+ function (i) { return buf[i].charCodeAt(0); } :
+ function (i) { return buf[i]; }
+
+ for (var len = 0, i = off; i < buf.length; i++) {
+ var cc = charCode(i);
+
+ if (cc == 0x3a) {
+ if (i == off) {
+ throw new Error('Invalid netstring with leading \':\'');
+ }
+
+ return len;
+ }
+
+ if (cc < 0x30 || cc > 0x39) {
+ throw new Error('Unexpected character \'' + buf[i] +
+ '\' found at offset ' + i
+ );
+ }
+
+ if (len == 0 && i > off) {
+ throw new Error('Invalid netstring with leading 0');
+ }
+
+ len = len * 10 + cc - 0x30;
+ }
+
+ assert.ok(i > off || off >= buf.length);
+
+ // We didn't get a complete length specification
+ if (i == buf.length) {
+ return -1;
+ }
+
+ return len;
+};
+exports.nsPayloadLength = nsPayloadLength;
+
+// Get the length of the netstring itself (i.e. including header and footer)
+// pointed to by Buffer or String 'buf'. Negative return values are the same
+// as length().
+var nsLength = function(buf, off) {
+ return nsLengthFromLength(nsPayloadLength(buf, off));
+};
+exports.nsLength = nsLength;
+
+// Get the netstring payload pointed to by the Buffer or String 'buf'.
+// Returns an object of the same type or a negative integer on exceptional
+// condition (same as nsPayloadLength())
+var nsPayload = function(buf, off) {
+ off = off || 0;
+
+ var len = nsPayloadLength(buf, off);
+ if (len < 0) {
+ return len;
+ }
+
+ var nsLen = nsLengthFromLength(len);
+
+ // sys.puts('len=' + len + '; nsLen=' + nsLen + '; buf.length=' + buf.length + '; off=' + off);
+
+ // We don't have the entire buffer yet
+ if (buf.length - off - nsLen < 0) {
+ return -1;
+ }
+
+ var start = off + (nsLen - len - 1);
+
+ if (typeof buf === 'string') {
+ return buf.substring(start, start + len);
+ } else if (typeof buf === 'object') {
+ return buf.slice(start, start + len);
+ }
+};
+exports.nsPayload = nsPayload;
+
+// Write the given payload to a netstring.
+//
+// All parameters other than 'pay' are optional; 'pay' itself can be either a
+// Buffer or a string. If 'payStart' is specified, the payload begins at this
+// offset, defaulting to 0 if unspecified. If 'payEnd' is specified, this is
+// offset of the first char (or byte, if 'pay' is a Buffer) that will be not
+// be written, defaulting to writing the entire string from 'payOff'. If
+// 'buf' is specified, the netstring is written to the given buffer, with a
+// string being returned by default. If 'bufOff' is specified, we start at
+// this offset in 'buf', defaulting to 0 is unspecified.
+//
+// If constructing a new string, the string is returned. If writing to a
+// buffer, the number of bytes consumed is returned, or -1 if there was not
+// enough space remaining in the buffer.
+var nsWrite = function(pay, payStart, payEnd, buf, bufOff) {
+ payStart = payStart || 0;
+ payEnd = (payEnd === undefined) ? pay.length : payEnd;
+ bufOff = bufOff || 0;
+
+ if (payStart < 0 || payStart >= pay.length) {
+ throw new Error('payStart is out of bounds');
+ }
+
+ if (payEnd > pay.length || payEnd < payStart) {
+ throw new Error('payEnd is out of bounds');
+ }
+
+ if (typeof pay !== 'string') {
+ throw new Error('Non-string payloads not yet supported');
+ }
+
+ if (buf) {
+ if (typeof buf !== 'object') {
+ throw new Error('The \'buf\' parameter must be a Buffer');
+ }
+
+ throw new Error('Writing to a Buffer not yet implemented');
+ } else {
+ return (payEnd - payStart) + ':' +
+ pay.substring(payStart, payEnd) +
+ ',';
+ }
+};
+exports.nsWrite = nsWrite;
+
+// Internal APIs
+
+var nsLengthFromLength = function(len) {
+ // Negative values are special (see nsPayloadLength()); just return it
+ if (len < 0) {
+ return len;
+ }
+
+ // Compute the number of digits in the length specifier. Stop at
+ // any value < 10 and just add 1 later (this catches the case where
+ // '0' requires a digit.
+ nslen = len;
+ while (len >= 10) {
+ nslen += 1;
+ len /= 10;
+ }
+
+ // nslen + 1 (last digit) + 1 (:) + 1 (,)
+ return nslen + 3;
+};
Oops, something went wrong. Retry.

0 comments on commit 82a52b5

Please sign in to comment.