(function(globalScope) {

    globalScope.tnetbin = {
        encode: function(obj) {
            switch (obj) {
            case null:
                return '0:~';
            case true:
                return '4:true!';
            case false:
                return '5:false!';
            }

            var type = typeof obj, s, tag;

            switch (type) {
            case 'string':
                s   = obj;
                tag = ',';
                break;
            case 'number':
                s = obj.toString();
                // Integer
                if (obj % 1 === 0)
                    tag = '#';
                // Float
                else
                    tag = '^';
                break;
            case 'object':
                if (obj instanceof ArrayBuffer) { // ArrayBuffer
                    s = String.fromCharCode.apply(null, new Uint16Array(obj));
                    tag = ',';
                } else if (obj instanceof Array) { // List
                    s = obj.map(tnetbin.encode).join('');
                    tag = ']';
                } else { // Object
                    var attrs = [];
                    for (var attr in obj) {
                        if (obj.hasOwnProperty(attr)) {
                            attrs.push(tnetbin.encode(attr),
                                       tnetbin.encode(obj[attr]));
                        }
                    }
                    s = attrs.join('');
                    tag = '}';
                }
            }

            return s.length + ':' + s + tag;
        },

        decode: function(data) {
            data = this.toArrayBuffer(data);
            result = decode1(data, 0);

            return {value: result.value, remain: remain(data, result.cursor)};
        },

        toArrayBuffer: function(data) {
            var view = new Uint16Array(data.length);
            // Transform the string to an array buffer
            for (var cursor = 0; cursor < data.length; cursor++)
                view[cursor] = data.charCodeAt(cursor);

            return view;
        }
    }

    COLON   = 58;
    ZERO    = 48;
    NULL    = 126;
    BOOLEAN = 33;
    INTEGER = 35;
    FLOAT   = 94;
    STRING  = 44;
    LIST    = 93;
    DICT    = 125;

    function decode1(data, cursor) {
        return decodeSize(data, cursor, function(data, cursor, size) {
            return decodeTag(data, cursor, size, function(data, cursor, size, tag) {
                switch (tag) {
                case NULL:
                    return decodeNull(data, cursor, size);
                case BOOLEAN:
                    return decodeBoolean(data, cursor, size);
                case INTEGER:
                    return decodeInteger(data, cursor, size);
                case FLOAT:
                    return decodeFloat(data, cursor, size);
                case STRING:
                    return decodeString(data, cursor, size);
                case LIST:
                    return decodeList(data, cursor, size);
                case DICT:
                    return decodeDict(data, cursor, size);
                default:
                    return {value: undefined, cursor: cursor + size + 1};
                }
            })
        });
    }

    function decodeSize(data, cursor, callback) {
        for (var size=0; data[cursor] != COLON; cursor++) {
            size = size*10 + (data[cursor] - ZERO);
        }

        return callback(data, cursor + 1, size);
    }

    function decodeTag(data, cursor, size, callback) {
        return callback(data, cursor, size, data[cursor + size]);
    }

    function remain(data, cursor) {
        var v = new Uint16Array(data.length - cursor);
        for (var i = 0, j = cursor; i < data.length; i++, j++)
            v[i] = data[j];
        return String.fromCharCode.apply(null, v);
    }


    function decodeNull(data, cursor) {
        return {value: null, cursor: cursor + 1};
    }

    function decodeBoolean(data, cursor, size) {
        return {value: (size === 4) ? true : false, cursor: cursor + size + 1};
    }

    function decodeInteger(data, cursor, size) {
        var end = cursor + size;
        for (var value = 0; cursor < end; cursor++)
            value = value*10 + (data[cursor] - ZERO);

        return {value: value, cursor: cursor + 1};
    }

    function decodeFloat(data, cursor, size) {
        var exp, end = cursor + size;

        for(var value = 0; data[cursor] != 46; cursor++)
            value = value*10 + (data[cursor] - ZERO);

        cursor++;
        for (var decimal = 0, exp=1; cursor < end; cursor++, exp*=10) {
            decimal = decimal*10 + (data[cursor] - ZERO);
        }

        return {value: value + (decimal/exp), cursor: cursor + 1};
    }

    function decodeString(data, cursor, size) {
        var d = new Uint16Array(size);

        for (var i = 0; i < size; i++, cursor++)
            d[i] = data[cursor];

        return {value: String.fromCharCode.apply(null, d), cursor: cursor + 1};
    }

    function decodeList(data, cursor, size) {
        if (size === 0)
            return {value: [], cursor: cursor};

        var list = [];
        var end  = cursor + size;
        var result;

        do {
            result = decode1(data, cursor);
            list.push(result.value);
            cursor = result.cursor;
        } while (cursor < end);

        return {value: list, cursor: result.cursor};
    }

    function decodeDict(data, cursor, size) {
        if (size === 0)
            return {value: {}, cursor: cursor + 1};

        var dict = {};

        var result = decodeList(data, cursor, size);
        for (var i = 0, items = result.value; i < items.length; i+=2)
            dict[items[i]] = items[i + 1];

        return {value: dict, cursor: result.cursor + 1};
    }

}(window));


@@ -3858,17 +3858,16 @@
 %0A    %7D%0A%0A
-%0A
     func
@@ -5142,16 +5142,20 @@
 : cursor
+ + 1
 %7D;%0A%0A    
@@ -5441,16 +5441,20 @@
 t.cursor
+ + 1
 %7D;%0A    %7D
@@ -5811,36 +5811,32 @@
 r: result.cursor
- + 1
 %7D;%0A    %7D%0A%0A%7D(wind

Fix cursor position for lists and dicts