1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 | 1
1
1
1
3
3
54
54
1
372
3
9
369
72
297
191
106
1
633
8
24
625
101
524
320
204
204
204
57
57
57
57
57
1094
1094
363
113
363
363
363
363
731
57
802
57
69
69
69
1050
1050
549
549
1233
549
549
69
40
69
8
8
8
128
128
60
60
117
60
60
81
8
8
3
3
8
1
| isEqual = require 'deep-equal'
r = require 'restructure'
CFFOperand = require './CFFOperand'
class CFFDict
constructor: (@ops = []) ->
@fields = {}
for field in @ops
key = if Array.isArray(field[0]) then field[0][0] << 8 | field[0][1] else field[0]
@fields[key] = field
decodeOperands = (type, stream, ret, operands) ->
if Array.isArray(type)
for op, i in operands
decodeOperands type[i], stream, ret, [op]
else if type.decode?
type.decode(stream, ret, operands)
else
switch type
when 'number', 'offset', 'sid'
operands[0]
when 'boolean'
!!operands[0]
else
operands
encodeOperands = (type, stream, ctx, operands) ->
if Array.isArray(type)
for op, i in operands
encodeOperands(type[i], stream, ctx, op)[0]
else if type.encode?
type.encode(stream, operands, ctx)
else if typeof operands is 'number'
[operands]
else Iif typeof operands is 'boolean'
[+operands]
else Eif Array.isArray(operands)
operands
else
[operands]
decode: (stream, parent) ->
end = stream.pos + parent.length
ret = {}
operands = []
# define hidden properties
Object.defineProperties ret,
parent: { value: parent }
_startOffset: { value: stream.pos }
while stream.pos < end
b = stream.readUInt8()
if b <= 21
if b is 12
b = (b << 8) | stream.readUInt8()
field = @fields[b]
Ithrow new Error "Unknown operator " + b unless field
ret[field[1]] = decodeOperands field[2], stream, ret, operands
operands = []
else
operands.push CFFOperand.decode(stream, b)
# fill in defaults
for key, field of @fields
ret[field[1]] ?= field[3]
return ret
size: (dict, parent, includePointers = true) ->
ctx =
parent: parent
val: dict
pointerSize: 0
startOffset: parent.startOffset or 0
len = 0
for k, field of @fields
val = dict[field[1]]
continue if not val? or isEqual val, field[3]
operands = encodeOperands field[2], null, ctx, val
for op in operands
len += CFFOperand.size op
key = if Array.isArray(field[0]) then field[0] else [field[0]]
len += key.length
if includePointers
len += ctx.pointerSize
return len
encode: (stream, dict, parent) ->
ctx =
pointers: []
startOffset: stream.pos
parent: parent
val: dict
pointerSize: 0
ctx.pointerOffset = stream.pos + @size(dict, ctx, false)
for field in @ops
val = dict[field[1]]
continue if not val? or isEqual val, field[3]
operands = encodeOperands field[2], stream, ctx, val
for op in operands
CFFOperand.encode stream, op
key = if Array.isArray(field[0]) then field[0] else [field[0]]
for op in key
stream.writeUInt8 op
i = 0
while i < ctx.pointers.length
ptr = ctx.pointers[i++]
ptr.type.encode(stream, ptr.val, ptr.parent)
return
module.exports = CFFDict
|