-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvariadic.go
241 lines (227 loc) · 9.9 KB
/
variadic.go
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
//////////////////////////////////////////////////////////////////
// //
// Copyright (c) 2018-2022 YottaDB LLC and/or its subsidiaries. //
// All rights reserved. //
// //
// This source code contains the intellectual property //
// of its copyright holder(s), and is made available //
// under a license. If you do not know the terms of //
// the license, please stop and do not read further. //
// //
//////////////////////////////////////////////////////////////////
package yottadb
import (
"fmt"
"runtime"
"strconv"
"sync/atomic"
"unsafe"
)
// #include <stdlib.h> /* For uint64_t definition on Linux */
// #include "libyottadb.h"
import "C"
const maxARM32RegParms uint32 = 4 // Max number of parms passed in registers in ARM32 (affects passing of 64 bit parms)
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Variadic plist support for C (despite Go [via cgo] not supporting it).
//
////////////////////////////////////////////////////////////////////////////////////////////////////
// variadicPlist structure is used to anchor the C parameter list used to call callg_nc() via
// ydb_call_variadic_list_func_st(). Because this structure's contents contain pointers to C
// allocated storage, this structure is NOT safe for concurrent access unless those accesses are
// setting different array elements and not affecting the overall structure.
type variadicPlist struct { // Variadic plist support (not exported) needed by LockS() function
cvplist *C.gparam_list
}
// alloc is a variadicPlist method to allocate the variable plist C structure anchored in variadicPlist.
func (vplist *variadicPlist) alloc() {
printEntry("variadicPlist.alloc()")
if nil == vplist {
panic("YDB: *variadicPlist receiver of alloc() cannot be nil")
}
if nil != vplist.cvplist {
// Already allocated
return
}
vplist.cvplist = (*C.gparam_list)(allocMem(C.size_t(C.sizeof_gparam_list)))
}
// callVariadicPlistFunc is a variadicPlist method to drive a variadic plist function with the given "vplist".
// The function pointer "vpfunc" must be to a C routine (ensured by caller) as cgo does not allow Go function
// pointers to be passed to C. Hence the unsafe pointer type usage below. Note this routine assumes that "vplist"
// is already initialized by a set-up call (a call to alloc() and one or more calls to setVPlistParam()).
func (vplist *variadicPlist) callVariadicPlistFunc(vpfunc unsafe.Pointer) int {
printEntry("variadicPlist.callVariadicPlistFunc()")
if nil == vplist {
panic("YDB: *variadicPlist receiver of callVariadicPlistFunc() cannot be nil")
}
if 1 != atomic.LoadUint32(&ydbInitialized) {
initializeYottaDB()
}
retval := int(C.ydb_call_variadic_plist_func((C.ydb_vplist_func)(vpfunc),
vplist.cvplist))
runtime.KeepAlive(vplist)
return retval
}
// free is a variadicPlist method to release the allocated C buffer in this structure.
func (vplist *variadicPlist) free() {
printEntry("variadicPlist.free()")
if (nil != vplist) && (nil != vplist.cvplist) {
freeMem(unsafe.Pointer(vplist.cvplist), C.size_t(C.sizeof_gparam_list))
vplist.cvplist = nil
}
}
// dump is a variadicPlist method to dump a variadic plist block for debugging purposes.
func (vplist *variadicPlist) dump(tptoken uint64, errstr *BufferT) {
printEntry("variadicPlist.dump()")
if nil == vplist {
panic("YDB: *variadicPlist receiver of dump() cannot be nil")
}
cvplist := vplist.cvplist
if nil == cvplist {
// Create an error to return
errmsg, err := MessageT(tptoken, errstr, (int)(YDB_ERR_STRUCTUNALLOCD))
if nil != err {
panic(fmt.Sprintf("YDB: Error fetching STRUCTUNALLOCD: %s", err))
}
fmt.Printf("YDB: Error fetching STRUCTUNALLOCD: %s\n", errmsg)
return
}
elemcnt := cvplist.n
fmt.Printf(" Total of %d (0x%x) elements in this variadic plist\n", elemcnt, elemcnt)
argbase := unsafe.Pointer(uintptr(unsafe.Pointer(cvplist)) + unsafe.Sizeof(cvplist))
for i := 0; i < int(elemcnt); i++ {
elemptr := unsafe.Pointer(uintptr(argbase) + (uintptr(i) * uintptr(unsafe.Sizeof(cvplist))))
fmt.Printf(" Elem %d (%p) Value: %d (0x%x)\n", i, elemptr, *((*uintptr)(elemptr)), *((*uintptr)(elemptr)))
}
runtime.KeepAlive(vplist)
}
// setUsed is a variadicPlist method to set the number of used elements in the variadic plist array.
func (vplist *variadicPlist) setUsed(tptoken uint64, errstr *BufferT, newUsed uint32) error {
printEntry("variadicPlist.setUsed")
if nil == vplist {
panic("YDB: *variadicPlist receiver of setUsed() cannot be nil")
}
cvplist := vplist.cvplist
if nil == cvplist {
// Create an error to return
errmsg, err := MessageT(tptoken, nil, (int)(YDB_ERR_STRUCTUNALLOCD))
if nil != err {
panic(fmt.Sprintf("YDB: Error fetching STRUCTUNALLOCD: %s", err))
}
return &YDBError{(int)(YDB_ERR_STRUCTUNALLOCD), errmsg}
}
if C.MAX_GPARAM_LIST_ARGS < newUsed {
panic(fmt.Sprintf("YDB: setUsed item count %d exceeds maximum count of %d", newUsed, C.MAX_GPARAM_LIST_ARGS))
}
cvplist.n = C.intptr_t(newUsed)
runtime.KeepAlive(vplist)
return nil
}
// setVPlistParam is a variadicPlist method to set an entry to the variable plist - note any addresses being passed in
// here MUST point to C allocated memory and NOT Go allocated memory or cgo will cause a panic. Note parameter
// indexes are 0 based.
func (vplist *variadicPlist) setVPlistParam(tptoken uint64, errstr *BufferT, paramindx uint32, paramaddr uintptr) error {
printEntry("variadicPlist.setVPlistParm")
if nil == vplist {
panic("YDB: *variadicPlist receiver of setVPlistParam() cannot be nil")
}
cvplist := vplist.cvplist
if nil == cvplist {
// Create an error to return
errmsg, err := MessageT(tptoken, errstr, (int)(YDB_ERR_STRUCTUNALLOCD))
if nil != err {
panic(fmt.Sprintf("YDB: Error fetching STRUCTUNALLOCD: %s", err))
}
return &YDBError{(int)(YDB_ERR_STRUCTUNALLOCD), errmsg}
}
if C.MAX_GPARAM_LIST_ARGS <= paramindx {
panic(fmt.Sprintf("YDB: setVPlistParam item count %d exceeds maximum count of %d", paramindx, C.MAX_GPARAM_LIST_ARGS))
}
// Compute address of indexed element
elemptr := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&cvplist.arg[0])) +
uintptr(paramindx*uint32(unsafe.Sizeof(paramaddr)))))
*elemptr = paramaddr
runtime.KeepAlive(vplist)
return nil
}
// setVPlistParam64Bit pushes the supplied 64 bit parameter onto the supplied variadicPlist at the supplied index. This is for
// parms that are ALWAYS 64 bits regardless of platform address mode. This is interesting only when using a 32 bit address
// mode which means the 64 bit parm must span 2 slots. In that case, the index is bumped by 2. Note that paramindx is incremented
// by this function since the caller does not know whether to bump it once or twice.
func (vplist *variadicPlist) setVPlistParam64Bit(tptoken uint64, errstr *BufferT, paramindx *uint32, parm64bit uint64) error {
var err error
printEntry("variadicPlist.setVPlistParm64Bit")
if nil == vplist {
panic("YDB: *variadicPlist receiver of setVPlistParam64Bit() cannot be nil")
}
cvplist := vplist.cvplist
if nil == cvplist {
// Create an error to return
errmsg, err := MessageT(tptoken, nil, (int)(YDB_ERR_STRUCTUNALLOCD))
if nil != err {
panic(fmt.Sprintf("YDB: Error fetching STRUCTUNALLOCD: %s", err))
}
return &YDBError{(int)(YDB_ERR_STRUCTUNALLOCD), errmsg}
}
if C.MAX_GPARAM_LIST_ARGS <= *paramindx {
panic(fmt.Sprintf("YDB: setVPlistParam64Bit item count %d exceeds maximum count of %d", paramindx,
C.MAX_GPARAM_LIST_ARGS))
}
// Compute address of indexed element(s)
if 64 == strconv.IntSize {
err = vplist.setVPlistParam(tptoken, errstr, *paramindx, uintptr(parm64bit))
if nil != err {
panic(fmt.Sprintf("YDB: Unknown error with varidicPlist.setVPlistParam(): %s", err))
}
} else {
// Have 32 bit addressing - 64 bit parm needs to take 2 slots (in the correct order).
if IsLittleEndian() {
if "arm" == runtime.GOARCH {
// If this is 32 bit ARM, there is a rule about the first 4 parms that go into registers. If
// there is a mix of 32 bit and 64 bit parameters, the 64 bit parm must always go into an
// even/odd pair of registers. If the next index is odd (meaning we could be loading into an
// odd/even pair, then that register is skipped and left unused. This only applies to parms
// loaded into registers and not to parms pushed on the stack.
if (1 == (*paramindx & 0x1)) && (maxARM32RegParms > *paramindx) {
// Our index is odd so skip a spot leaving garbage contents in that slot rather than
// take the time to clear them.
(*paramindx)++
}
if C.MAX_GPARAM_LIST_ARGS <= *paramindx {
panic(fmt.Sprintf("YDB: setVPlistParam64Bit item count %d exceeds maximum count of %d",
paramindx, C.MAX_GPARAM_LIST_ARGS))
}
}
err = vplist.setVPlistParam(tptoken, errstr, *paramindx, uintptr(parm64bit&0xffffffff))
if nil != err {
panic(fmt.Sprintf("YDB: Unknown error with varidicPlist.setVPlistParam(): %s", err))
}
(*paramindx)++
if C.MAX_GPARAM_LIST_ARGS <= *paramindx {
panic(fmt.Sprintf("YDB: setVPlistParam64Bit item count %d exceeds maximum count of %d",
paramindx, C.MAX_GPARAM_LIST_ARGS))
}
err = vplist.setVPlistParam(tptoken, errstr, *paramindx, uintptr(parm64bit>>32))
if nil != err {
panic(fmt.Sprintf("YDB: Unknown error with varidicPlist.setVPlistParam(): %s", err))
}
} else {
err = vplist.setVPlistParam(tptoken, errstr, *paramindx, uintptr(parm64bit>>32))
if nil != err {
panic(fmt.Sprintf("YDB: Unknown error with varidicPlist.setVPlistParam(): %s", err))
}
(*paramindx)++
if C.MAX_GPARAM_LIST_ARGS <= *paramindx {
panic(fmt.Sprintf("YDB: setVPlistParam64Bit item count %d exceeds maximum count of %d",
paramindx, C.MAX_GPARAM_LIST_ARGS))
}
err = vplist.setVPlistParam(tptoken, errstr, *paramindx, uintptr(parm64bit&0xffffffff))
if nil != err {
panic(fmt.Sprintf("YDB: Unknown error with varidicPlist.setVPlistParam(): %s", err))
}
}
}
(*paramindx)++
runtime.KeepAlive(vplist)
return nil
}