utils = require './utils' class Pointer constructor: (@offsetType, @type, @options = {}) -> @type = null if @type is 'void' @options.type ?= 'local' @options.allowNull ?= true @options.nullValue ?= 0 @options.lazy ?= false if @options.relativeTo @relativeToGetter = new Function('ctx', "return ctx.#{@options.relativeTo}") decode: (stream, ctx) -> offset = @offsetType.decode(stream, ctx) # handle NULL pointers if offset is @options.nullValue and @options.allowNull return null relative = switch @options.type when 'local' then ctx._startOffset when 'immediate' then stream.pos - @offsetType.size() when 'parent' then ctx.parent._startOffset else c = ctx while c.parent c = c.parent c._startOffset or 0 if @options.relativeTo relative += @relativeToGetter ctx ptr = offset + relative if @type? val = null decodeValue = => return val if val? pos = stream.pos stream.pos = ptr val = @type.decode(stream, ctx) stream.pos = pos return val # If this is a lazy pointer, define a getter to decode only when needed. # This obviously only works when the pointer is contained by a Struct. if @options.lazy return new utils.PropertyDescriptor get: decodeValue return decodeValue() else return ptr size: (val, ctx) -> parent = ctx switch @options.type when 'local', 'immediate' break when 'parent' ctx = ctx.parent else # global while ctx.parent ctx = ctx.parent type = @type unless type? unless val instanceof VoidPointer throw new Error "Must be a VoidPointer" type = val.type val = val.value if val and ctx ctx.pointerSize += type.size(val, parent) return @offsetType.size() encode: (stream, val, ctx) -> parent = ctx if not val? @offsetType.encode(stream, @options.nullValue) return switch @options.type when 'local' relative = ctx.startOffset when 'immediate' relative = stream.pos + @offsetType.size(val, parent) when 'parent' ctx = ctx.parent relative = ctx.startOffset else # global relative = 0 while ctx.parent ctx = ctx.parent if @options.relativeTo relative += @relativeToGetter parent.val @offsetType.encode(stream, ctx.pointerOffset - relative) type = @type unless type? unless val instanceof VoidPointer throw new Error "Must be a VoidPointer" type = val.type val = val.value ctx.pointers.push type: type val: val parent: parent ctx.pointerOffset += type.size(val, parent) # A pointer whose type is determined at decode time class VoidPointer constructor: (@type, @value) -> exports.Pointer = Pointer exports.VoidPointer = VoidPointer