~bigbes/tarantool

tarantool-protobuf

ref: 7b3d69010b4ebad701bb98c0dda9e785823857d0 tarantool-protobuf/bench/c_accel/prim_ffi.lua -rw-r--r-- 5.9 KiB
7b3d6901 — Eugene Blikh bench: apply mcode arena hardening across all bench scripts (3qu) 2 months ago
                                                                                
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
-- prim_ffi.lua -- Strategy 2: per-primitive FFI bindings.
--
-- Dispatch (per-field branch logic) is in Lua. Each wire primitive
-- crosses the FFI boundary into prim.c. Used by spike_bench.lua via
-- require('prim_ffi').

local ffi = require('ffi')
local bit = require('bit')

ffi.cdef[[
    typedef struct ibuf_s {
        uint8_t *data;
        size_t   len;
        size_t   cap;
    } ibuf_t;

    void pb_ibuf_init(ibuf_t *b);
    void pb_ibuf_free(ibuf_t *b);
    void pb_ibuf_reset(ibuf_t *b);

    void pb_write_varint(ibuf_t *b, uint64_t v);
    void pb_write_bytes(ibuf_t *b, const uint8_t *src, size_t n);
    void pb_write_string_field(ibuf_t *b, uint32_t tag,
                               const uint8_t *src, size_t n);
    const uint8_t *pb_read_varint(const uint8_t *p,
                                  const uint8_t *end, uint64_t *out);
]]

local function find_lib()
    local SCRIPT_DIR = (debug.getinfo(1, 'S').source:match('@?(.*/)') or './')
    local UNAME = io.popen('uname -s'):read('*l')
    local ext = (UNAME == 'Darwin') and '.dylib' or '.so'
    return SCRIPT_DIR .. 'libpb_prim' .. ext
end

local C = ffi.load(find_lib())

local outbuf = ffi.new('ibuf_t')
C.pb_ibuf_init(outbuf)
local subbuf = ffi.new('ibuf_t')
C.pb_ibuf_init(subbuf)
local sub2buf = ffi.new('ibuf_t')  -- for one extra level of nesting
C.pb_ibuf_init(sub2buf)

local v_out = ffi.new('uint64_t[1]')

local rshift, band = bit.rshift, bit.band

local M = {}

-- ----------------------------------------------------------------
-- Address (sub-message used by Person.address) encode/decode helpers
-- ----------------------------------------------------------------

local function address_encode_into(buf, t)
    if t.street then
        C.pb_write_string_field(buf, 0x0A, t.street, #t.street)
    end
    if t.city then
        C.pb_write_string_field(buf, 0x12, t.city, #t.city)
    end
    if t.zip then
        C.pb_write_varint(buf, 0x18)
        C.pb_write_varint(buf, t.zip)
    end
end

-- ----------------------------------------------------------------
-- Person encode
-- ----------------------------------------------------------------

function M.Person_encode(t)
    C.pb_ibuf_reset(outbuf)
    if t.name then
        C.pb_write_string_field(outbuf, 0x0A, t.name, #t.name)
    end
    if t.age then
        C.pb_write_varint(outbuf, 0x10)
        C.pb_write_varint(outbuf, t.age)
    end
    if t.emails then
        local emails = t.emails
        for i = 1, #emails do
            local e = emails[i]
            C.pb_write_string_field(outbuf, 0x1A, e, #e)
        end
    end
    if t.address then
        C.pb_ibuf_reset(subbuf)
        address_encode_into(subbuf, t.address)
        C.pb_write_varint(outbuf, 0x2A)
        C.pb_write_varint(outbuf, subbuf.len)
        C.pb_write_bytes(outbuf, subbuf.data, subbuf.len)
    end
    if t.lucky_numbers then
        C.pb_ibuf_reset(subbuf)
        local lucky = t.lucky_numbers
        for i = 1, #lucky do
            C.pb_write_varint(subbuf, lucky[i])
        end
        C.pb_write_varint(outbuf, 0x3A)
        C.pb_write_varint(outbuf, subbuf.len)
        C.pb_write_bytes(outbuf, subbuf.data, subbuf.len)
    end
    return ffi.string(outbuf.data, outbuf.len)
end

-- ----------------------------------------------------------------
-- Person decode
-- ----------------------------------------------------------------

local function decode_address(p, endp)
    local result = {}
    while p < endp do
        p = C.pb_read_varint(p, endp, v_out)
        if p == nil then break end
        local tag = tonumber(v_out[0])
        local field = rshift(tag, 3)
        local wt    = band(tag, 7)
        if wt == 2 then
            p = C.pb_read_varint(p, endp, v_out)
            local slen = tonumber(v_out[0])
            if field == 1 then
                result.street = ffi.string(p, slen)
            elseif field == 2 then
                result.city = ffi.string(p, slen)
            end
            p = p + slen
        elseif wt == 0 then
            p = C.pb_read_varint(p, endp, v_out)
            if field == 3 then
                result.zip = tonumber(v_out[0])
            end
        else
            break
        end
    end
    return result
end

function M.Person_decode(s)
    local p    = ffi.cast('const uint8_t*', s)
    local endp = p + #s
    local result  = {}
    local emails  = nil
    local n_emails = 0
    local lucky   = nil
    local n_lucky = 0

    while p < endp do
        p = C.pb_read_varint(p, endp, v_out)
        if p == nil then break end
        local tag = tonumber(v_out[0])
        local field = rshift(tag, 3)
        local wt    = band(tag, 7)
        if wt == 2 then
            p = C.pb_read_varint(p, endp, v_out)
            local slen = tonumber(v_out[0])
            if field == 1 then
                result.name = ffi.string(p, slen)
                p = p + slen
            elseif field == 3 then
                if emails == nil then emails = {} end
                n_emails = n_emails + 1
                emails[n_emails] = ffi.string(p, slen)
                p = p + slen
            elseif field == 5 then
                result.address = decode_address(p, p + slen)
                p = p + slen
            elseif field == 7 then
                if lucky == nil then lucky = {} end
                local fend = p + slen
                while p < fend do
                    p = C.pb_read_varint(p, fend, v_out)
                    if p == nil then break end
                    n_lucky = n_lucky + 1
                    lucky[n_lucky] = tonumber(v_out[0])
                end
            else
                p = p + slen
            end
        elseif wt == 0 then
            p = C.pb_read_varint(p, endp, v_out)
            if field == 2 then
                result.age = tonumber(v_out[0])
            end
        else
            break
        end
    end

    if emails then result.emails = emails end
    if lucky  then result.lucky_numbers = lucky end
    return result
end

return M