pointer in progress

main
Matthew Butterick 8 years ago
parent 8c3deb14ec
commit af42fdcf99

@ -6,43 +6,358 @@ approximates
https://github.com/mbutterick/restructure/blob/master/test/Pointer.coffee
|#
;; it 'should handle null pointers', ->
(let ([stream (+DecodeStream (bytes 0))]
;describe 'Pointer', ->
; describe 'decode', ->
; it 'should handle null pointers', ->
; stream = new DecodeStream new Buffer [0]
; pointer = new Pointer uint8, uint8
; should.not.exist pointer.decode(stream, _startOffset: 50)
(let ([stream (+DecodeStream (+Buffer '(0)))]
[pointer (+Pointer uint8 uint8)])
(check-exn exn:fail? (λ () (send pointer decode stream (mhash '_startOffset 50)))))
(check-false (send pointer decode stream (mhash '_startOffset 50))))
;
; it 'should use local offsets from start of parent by default', ->
; stream = new DecodeStream new Buffer [1, 53]
; pointer = new Pointer uint8, uint8
; pointer.decode(stream, _startOffset: 0).should.equal 53
;; it 'should use local offsets from start of parent by default', ->
(let ([stream (+DecodeStream (bytes 1 53))]
(let ([stream (+DecodeStream (+Buffer '(1 53)))]
[pointer (+Pointer uint8 uint8)])
(check-equal? (send pointer decode stream (mhash '_startOffset 0)) 53))
;; it 'should support immediate offsets', ->
(let ([stream (+DecodeStream (bytes 1 53))]
[pointer (+Pointer uint8 uint8 'immediate)])
;
; it 'should support immediate offsets', ->
; stream = new DecodeStream new Buffer [1, 53]
; pointer = new Pointer uint8, uint8, type: 'immediate'
; pointer.decode(stream).should.equal 53
(let ([stream (+DecodeStream (+Buffer '(1 53)))]
[pointer (+Pointer uint8 uint8 (mhash 'type 'immediate))])
(check-equal? (send pointer decode stream) 53))
;; it 'should support offsets relative to the parent', ->
(let ([stream (+DecodeStream (bytes 0 0 1 53))]
[pointer (+Pointer uint8 uint8 'parent)])
;
; it 'should support offsets relative to the parent', ->
; stream = new DecodeStream new Buffer [0, 0, 1, 53]
; stream.pos = 2
; pointer = new Pointer uint8, uint8, type: 'parent'
; pointer.decode(stream, parent: _startOffset: 2).should.equal 53
(let ([stream (+DecodeStream (+Buffer '(0 0 1 53)))]
[pointer (+Pointer uint8 uint8 (mhash 'type 'parent))])
(send stream pos 2)
(check-equal? (send pointer decode stream (mhash 'parent (mhash '_startOffset 2))) 53))
;; it 'should support global offsets', ->
(let ([stream (+DecodeStream (bytes 1 2 4 0 0 0 53))]
[pointer (+Pointer uint8 uint8 'global)])
;
; it 'should support global offsets', ->
; stream = new DecodeStream new Buffer [1, 2, 4, 0, 0, 0, 53]
; pointer = new Pointer uint8, uint8, type: 'global'
; stream.pos = 2
; pointer.decode(stream, parent: parent: _startOffset: 2).should.equal 53
(let ([stream (+DecodeStream (+Buffer '(1 2 4 0 0 0 53)))]
[pointer (+Pointer uint8 uint8 (mhash 'type 'global))])
(send stream pos 2)
(check-equal? (send pointer decode stream (mhash 'parent (mhash 'parent (mhash '_startOffset 2)))) 53))
;; todo
#|
it 'should support offsets relative to a property on the parent', ->
stream = new DecodeStream new Buffer [1, 0, 0, 0, 0, 53]
pointer = new Pointer uint8, uint8, relativeTo: 'parent.ptr'
pointer.decode(stream, _startOffset: 0, parent: ptr: 4).should.equal 53
|#
;; it 'should support returning pointer if there is no decode type', ->
(let ([stream (+DecodeStream (bytes 4))]
; it 'should support offsets relative to a property on the parent', ->
; stream = new DecodeStream new Buffer [1, 0, 0, 0, 0, 53]
; pointer = new Pointer uint8, uint8, relativeTo: 'parent.ptr'
; pointer.decode(stream, _startOffset: 0, parent: ptr: 4).should.equal 53
(let ([stream (+DecodeStream (+Buffer '(1 0 0 0 0 53)))]
[pointer (+Pointer uint8 uint8 (mhash 'relativeTo (λ (ctx) (· ctx parent ptr))))])
(check-equal? (send pointer decode stream (mhash '_startOffset 0 'parent (mhash 'ptr 4))) 53))
;
; it 'should support returning pointer if there is no decode type', ->
; stream = new DecodeStream new Buffer [4]
; pointer = new Pointer uint8, 'void'
; pointer.decode(stream, _startOffset: 0).should.equal 4
(let ([stream (+DecodeStream (+Buffer '(4)))]
[pointer (+Pointer uint8 'void)])
(check-equal? (send pointer decode stream (mhash '_startOffset 0)) 4))
; todo: skipped lazy pointers for now
; it 'should support decoding pointers lazily', ->
; stream = new DecodeStream new Buffer [1, 53]
; struct = new Struct
; ptr: new Pointer uint8, uint8, lazy: yes
;
; res = struct.decode(stream)
; Object.getOwnPropertyDescriptor(res, 'ptr').get.should.be.a('function')
; Object.getOwnPropertyDescriptor(res, 'ptr').enumerable.should.equal(true)
; res.ptr.should.equal 53
;
; describe 'size', ->
; it 'should add to local pointerSize', ->
; pointer = new Pointer uint8, uint8
; ctx = pointerSize: 0
; pointer.size(10, ctx).should.equal 1
; ctx.pointerSize.should.equal 1
(let ([pointer (+Pointer uint8 uint8)]
[ctx (mhash 'pointerSize 0)])
(check-equal? (send pointer size 10 ctx) 1)
(check-equal? (ref ctx 'pointerSize) 1))
;
; it 'should add to immediate pointerSize', ->
; pointer = new Pointer uint8, uint8, type: 'immediate'
; ctx = pointerSize: 0
; pointer.size(10, ctx).should.equal 1
; ctx.pointerSize.should.equal 1
(let ([pointer (+Pointer uint8 uint8 (mhash 'type 'immediate))]
[ctx (mhash 'pointerSize 0)])
(check-equal? (send pointer size 10 ctx) 1)
(check-equal? (ref ctx 'pointerSize) 1))
;
; it 'should add to parent pointerSize', ->
; pointer = new Pointer uint8, uint8, type: 'parent'
; ctx = parent: pointerSize: 0
; pointer.size(10, ctx).should.equal 1
; ctx.parent.pointerSize.should.equal 1
(let ([pointer (+Pointer uint8 uint8 (mhash 'type 'parent))]
[ctx (mhash 'parent (mhash 'pointerSize 0))])
(check-equal? (send pointer size 10 ctx) 1)
(check-equal? (ref* ctx 'parent 'pointerSize) 1))
;
; it 'should add to global pointerSize', ->
; pointer = new Pointer uint8, uint8, type: 'global'
; ctx = parent: parent: parent: pointerSize: 0
; pointer.size(10, ctx).should.equal 1
; ctx.parent.parent.parent.pointerSize.should.equal 1
(let ([pointer (+Pointer uint8 uint8 (mhash 'type 'global))]
[ctx (mhash 'parent (mhash 'parent (mhash 'parent (mhash 'pointerSize 0))))])
(check-equal? (send pointer size 10 ctx) 1)
(check-equal? (ref* ctx 'parent 'parent 'parent 'pointerSize) 1))
; todo: void pointer
; it 'should handle void pointers', ->
; pointer = new Pointer uint8, 'void'
; ctx = pointerSize: 0
; pointer.size(new VoidPointer(uint8, 50), ctx).should.equal 1
; ctx.pointerSize.should.equal 1
;
; it 'should throw if no type and not a void pointer', ->
; pointer = new Pointer uint8, 'void'
; ctx = pointerSize: 0
; should.throw ->
; pointer.size(30, ctx).should.equal 1
;
; it 'should return a fixed size without a value', ->
; pointer = new Pointer uint8, uint8
; pointer.size().should.equal 1
(let ([pointer (+Pointer uint8 uint8)])
(check-equal? (send pointer size) 1))
;
; describe 'encode', ->
; it 'should handle null pointers', (done) ->
; stream = new EncodeStream
; stream.pipe concat (buf) ->
; buf.should.deep.equal new Buffer [0]
; done()
;
; ptr = new Pointer uint8, uint8
; ctx =
; pointerSize: 0,
; startOffset: 0,
; pointerOffset: 0,
; pointers: []
;
; ptr.encode(stream, null, ctx)
; ctx.pointerSize.should.equal 0
;
; stream.end()
;
; it 'should handle local offsets', (done) ->
; stream = new EncodeStream
; stream.pipe concat (buf) ->
; buf.should.deep.equal new Buffer [1]
; done()
;
; ptr = new Pointer uint8, uint8
; ctx =
; pointerSize: 0,
; startOffset: 0,
; pointerOffset: 1,
; pointers: []
;
; ptr.encode(stream, 10, ctx)
; ctx.pointerOffset.should.equal 2
; ctx.pointers.should.deep.equal [
; { type: uint8, val: 10, parent: ctx }
; ]
;
; stream.end()
;
; it 'should handle immediate offsets', (done) ->
; stream = new EncodeStream
; stream.pipe concat (buf) ->
; buf.should.deep.equal new Buffer [0]
; done()
;
; ptr = new Pointer uint8, uint8, type: 'immediate'
; ctx =
; pointerSize: 0,
; startOffset: 0,
; pointerOffset: 1,
; pointers: []
;
; ptr.encode(stream, 10, ctx)
; ctx.pointerOffset.should.equal 2
; ctx.pointers.should.deep.equal [
; { type: uint8, val: 10, parent: ctx }
; ]
;
; stream.end()
;
; it 'should handle immediate offsets', (done) ->
; stream = new EncodeStream
; stream.pipe concat (buf) ->
; buf.should.deep.equal new Buffer [0]
; done()
;
; ptr = new Pointer uint8, uint8, type: 'immediate'
; ctx =
; pointerSize: 0,
; startOffset: 0,
; pointerOffset: 1,
; pointers: []
;
; ptr.encode(stream, 10, ctx)
; ctx.pointerOffset.should.equal 2
; ctx.pointers.should.deep.equal [
; { type: uint8, val: 10, parent: ctx }
; ]
;
; stream.end()
;
; it 'should handle offsets relative to parent', (done) ->
; stream = new EncodeStream
; stream.pipe concat (buf) ->
; buf.should.deep.equal new Buffer [2]
; done()
;
; ptr = new Pointer uint8, uint8, type: 'parent'
; ctx =
; parent:
; pointerSize: 0,
; startOffset: 3,
; pointerOffset: 5,
; pointers: []
;
; ptr.encode(stream, 10, ctx)
; ctx.parent.pointerOffset.should.equal 6
; ctx.parent.pointers.should.deep.equal [
; { type: uint8, val: 10, parent: ctx }
; ]
;
; stream.end()
;
; it 'should handle global offsets', (done) ->
; stream = new EncodeStream
; stream.pipe concat (buf) ->
; buf.should.deep.equal new Buffer [5]
; done()
;
; ptr = new Pointer uint8, uint8, type: 'global'
; ctx =
; parent:
; parent:
; parent:
; pointerSize: 0,
; startOffset: 3,
; pointerOffset: 5,
; pointers: []
;
; ptr.encode(stream, 10, ctx)
; ctx.parent.parent.parent.pointerOffset.should.equal 6
; ctx.parent.parent.parent.pointers.should.deep.equal [
; { type: uint8, val: 10, parent: ctx }
; ]
;
; stream.end()
;
; it 'should support offsets relative to a property on the parent', (done) ->
; stream = new EncodeStream
; stream.pipe concat (buf) ->
; buf.should.deep.equal new Buffer [6]
; done()
;
; ptr = new Pointer uint8, uint8, relativeTo: 'ptr'
; ctx =
; pointerSize: 0,
; startOffset: 0,
; pointerOffset: 10,
; pointers: []
; val:
; ptr: 4
;
; ptr.encode(stream, 10, ctx)
; ctx.pointerOffset.should.equal 11
; ctx.pointers.should.deep.equal [
; { type: uint8, val: 10, parent: ctx }
; ]
;
; stream.end()
;
; it 'should support void pointers', (done) ->
; stream = new EncodeStream
; stream.pipe concat (buf) ->
; buf.should.deep.equal new Buffer [1]
; done()
;
; ptr = new Pointer uint8, 'void'
; ctx =
; pointerSize: 0,
; startOffset: 0,
; pointerOffset: 1,
; pointers: []
;
; ptr.encode(stream, new VoidPointer(uint8, 55), ctx)
; ctx.pointerOffset.should.equal 2
; ctx.pointers.should.deep.equal [
; { type: uint8, val: 55, parent: ctx }
; ]
;
; stream.end()
;
; it 'should throw if not a void pointer instance', ->
; stream = new EncodeStream
; ptr = new Pointer uint8, 'void'
; ctx =
; pointerSize: 0,
; startOffset: 0,
; pointerOffset: 1,
; pointers: []
;
; should.throw ->
; ptr.encode(stream, 44, ctx)

@ -6,55 +6,82 @@ approximates
https://github.com/mbutterick/restructure/blob/master/src/Pointer.coffee
|#
(define-subclass object% (Pointer offsetType type [scope 'local])
(define-subclass object% (Pointer offsetType type [options (mhash)])
(when (eq? type 'void) (set! type #f))
(and (symbol? scope) (caseq scope
[(local parent immediate global) 'yay]
[else (raise-argument-error 'Pointer "local or parent or immediate" scope)]))
(hash-ref! options 'type 'local)
(hash-ref! options 'allowNull #t)
(hash-ref! options 'nullValue 0)
(hash-ref! options 'lazy #f)
(define relativeToGetter (ref options 'relativeTo)) ; change this to a simple lambda
(define/public (decode stream [ctx #f])
(define offset (send offsetType decode stream ctx))
(report scope 'pointer-scope)
(define relative (caseq scope
[(local) #;(when (and (· ctx res _startOffset) (· ctx _startOffset)
(not (= (· ctx res _startOffset) (· ctx _startOffset))))
(report* ctx (· ctx res _startOffset) (· ctx _startOffset))
(error 'bazongas))
(· ctx res _startOffset)]
[(parent) (· ctx res parent res _startOffset)]
[(immediate) (- (· stream pos) (send offsetType size))]
[(global)
(let loop ([c ctx])
(cond
[(· c parent) => loop]
[(· c _startOffset)]
[else 0]))]))
(report* this (· this _startOffset)
(and (· this res) (· this res _startOffset))
ctx
(and (· ctx res) (· ctx res _startOffset)))
#;(when (and ctx (· ctx res _startOffset) (= (· ctx res _startOffset) 1012)) (error 'stop))
(report* offset relative)
(define ptr (+ offset relative))
(report* ptr)
(report (send offsetType size) 'size)
(cond
[type (define orig-pos (send stream pos))
(send stream pos ptr)
(define val (send type decode stream ctx))
(send stream pos orig-pos)
val]
[else ptr]))
;; handle NULL pointers
[(and (eq? offset (ref options 'nullValue)) (ref options 'allowNull)) #f]
[else
(define relative (caseq (ref options 'type)
[(local) (ref ctx '_startOffset)]
[(immediate) (- (· stream pos) (send offsetType size))]
[(parent) (ref* ctx 'parent '_startOffset)]
[else (let loop ([ctx ctx])
(cond
[(· ctx parent) => loop]
[(ref ctx '_startOffset)]
[else 0]))]))
(when (ref options 'relativeTo)
; relativeToGetter only defined if 'relativeTo key exists, so this is safe
(increment! relative (relativeToGetter ctx)))
(define ptr (+ offset relative))
(cond
[type (define val #f)
(define (decodeValue)
(cond
[val]
[else (define pos (· stream pos))
(send stream pos ptr)
(define val (send type decode stream ctx))
(send stream pos pos)
val]))
;; skip lazy pointer chores
(decodeValue)]
[else ptr])]))
(define/public (size [val #f] [ctx #f])
(error 'Pointer-size-not-done)
(report* this offsetType type (send type size)))
(define parent ctx)
(caseq (ref options 'type)
[(local immediate) (void)]
[(parent) (set! ctx (ref ctx 'parent))]
[else ; global
(set! ctx (let loop ([ctx ctx])
(cond
[(ref ctx 'parent) => loop]
[else ctx])))])
(define type_ type)
(unless type_
; todo: uncomment when VoidPointer class is ready
#;(unless (VoidPointer? val)
(raise-argument-error 'Pointer:size "VoidPointer" val))
(set! type (ref val 'type))
(set! val (ref val 'value)))
(when (and val ctx)
(ref-set! ctx 'pointerSize (+ (ref ctx 'pointerSize) (send type size val parent))))
(send offsetType size))
(define/public (encode stream val)
(error 'Pointer-encode-not-done))
#;(define/public (encode stream val)
(error 'Pointer-encode-not-done))

Loading…
Cancel
Save