1local m = {} -- the module table
2
3local mt = {} -- the module metatable
4
5-- given a binary array, set a metamethod to return its length
6-- (e.g., #binaryArray, calls this)
7function mt:__len()
8    return self.size
9end
10
11-- Create a new binary array of an initial size
12function m.New(sizeOrString)
13    -- the array storage itself
14    local o = {}
15
16    if type(sizeOrString) == "string" then
17        o.str = sizeOrString
18        o.size = #sizeOrString
19    elseif type(sizeOrString) == "number" then
20        o.data = {}
21        o.size = sizeOrString
22    else
23        error("Expect a integer size value or string to construct a binary array")
24    end
25    -- set the inheritance
26    setmetatable(o, {__index = mt, __len = mt.__len})
27    return o
28end
29
30-- Get a slice of the binary array from start to end position
31function mt:Slice(startPos, endPos)
32    startPos = startPos or 0
33    endPos = endPos or self.size
34    local d = self.data
35    if d then
36        -- if the self.data is defined, we are building the buffer
37        -- in a Lua table
38
39        -- new table to store the slice components
40        local b = {}
41
42        -- starting with the startPos, put all
43        -- values into the new table to be concat later
44        -- updated the startPos based on the size of the
45        -- value
46        while startPos < endPos do
47            local v = d[startPos] or '/0'
48            table.insert(b, v)
49            startPos = startPos + #v
50        end
51
52        -- combine the table of strings into one string
53        -- this is faster than doing a bunch of concats by themselves
54        return table.concat(b)
55    else
56        -- n.b start/endPos are 0-based incoming, so need to convert
57        --     correctly. in python a slice includes start -> end - 1
58        return self.str:sub(startPos+1, endPos)
59    end
60end
61
62-- Grow the binary array to a new size, placing the exisiting data
63-- at then end of the new array
64function mt:Grow(newsize)
65    -- the new table to store the data
66    local newT = {}
67
68    -- the offset to be applied to existing entries
69    local offset = newsize - self.size
70
71    -- loop over all the current entries and
72    -- add them to the new table at the correct
73    -- offset location
74    local d = self.data
75    for i,data in pairs(d) do
76        newT[i + offset] = data
77    end
78
79    -- update this storage with the new table and size
80    self.data = newT
81    self.size = newsize
82end
83
84-- memorization for padding strings
85local pads = {}
86
87-- pad the binary with n \0 bytes at the starting position
88function mt:Pad(n, startPos)
89    -- use memorization to avoid creating a bunch of strings
90    -- all the time
91    local s = pads[n]
92    if not s then
93        s = string.rep('\0', n)
94        pads[n] = s
95    end
96
97    -- store the padding string at the start position in the
98    -- Lua table
99    self.data[startPos] = s
100end
101
102-- Sets the binary array value at the specified position
103function mt:Set(value, position)
104    self.data[position] = value
105end
106
107-- locals for slightly faster access
108local sunpack = string.unpack
109local spack = string.pack
110
111-- Pack the data into a binary representation
112function m.Pack(fmt, ...)
113    return spack(fmt, ...)
114end
115
116-- Unpack the data from a binary representation in
117-- a Lua value
118function m.Unpack(fmt, s, pos)
119    return sunpack(fmt, s.str, pos + 1)
120end
121
122-- Return the binary array module
123return m