-
Notifications
You must be signed in to change notification settings - Fork 8
/
buffers.h
423 lines (390 loc) · 18.1 KB
/
buffers.h
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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
/* buffers.h -- Version 1.11 */
/* The following abbreviations are used at start of parameter names
* to indicate the type of data:
* s string (char * or WCHAR *) [PV]
* sw wide string (WCHAR *) [PV]
* p pointer (usually to some structure) [PV]
* a array (packed array as in C) (usually of some structure) [PV]
* called a "vector" or "vect" in some places.
* n generic number [IV, UV, or NV]
* iv signed integral value [IV]
* u unsigned integral value [UV]
* d floating-point number (double) [NV]
* b boolean (bool) [IV]
* c count of items [UV]
* l length (in bytes) [UV]
* lw length in WCHARs [UV]
* h a handle [IV]
* r record (structure) [PV]
* sv Perl scalar (s, i, u, d, n, or rv) [SV]
* rv Perl reference (usually to scalar) [RV]
* hv reference to Perl hash [HV]
* av reference to Perl array [AV]
* cv Perl code reference [PVCV]
*
* Unusual combined types:
* pp single pointer (to non-Perl data) packed into string [PV]
* pap vector of pointers (to non-Perl data) packed into string [PV]
*
* Whether a parameter is for input data, output data, or both is usually
* not reflected by the data type prefix. In cases where this is not
* obvious nor reflected in the variable name proper, you can use
* the following in front of the data type prefix:
* i an input parameter given to API (usually omitted)
* o an Output parameter taken from API
* io Input given to API then overwritten with Output taken from API
*/
/* Buffer arguments are usually followed by an argument (or two) specifying
* their size and/or returning the size of data written. The size can be
* measured in bytes ["lSize"] or in characters [for (char *) buffers such as
* for *A() routines, these sizes are also called "lSize", but are called
* "lwSize" for (WCHAR *) buffers, UNICODE strings, such as for *W() routines].
*
* Before calling the actual C function, you must make sure the Perl variable
* actually has a big enough buffer allocated, and, if the user didn't want
* to specify a buffer size, set the buffer size to be correct. This is what
* the grow_*() macros are for. They also handle special meanings of the
* buffer size argument [described below].
*
* Once the actual C function returns, you must set the Perl variable to know
* the size of the written data. This is what the trunc_*() macros are for.
*
* The size sometimes does and sometimes doesn't include the trailing '\0'
* [or L'\0'], so we always add or subtract 1 in the appropriate places so
* we don't care about this detail.
*
* A call may 1) request a pointer to the buffer size which means that
* the buffer size will be overwritten with the size of the data written;
* 2) have an extra argument which is a pointer to the place to write the
* size of the written data; 3) provide the size of the written data in
* the function's return value; 4) format the data so that the length
* can be determined by examining the data [such as with '\0'-terminated
* strings]; or 5) write fixed-length data [usually sizeof(STRUCT)].
* This obviously determines what you should use in the trunc_*() macro
# to specify the size of the output value.
*
* The user can pass in an empty list reference, C<[]>, to indicate C<NULL>
* for the pointer to the buffer which means that they don't want that data.
*
* The user can pass in C<[]> or C<0> to indicate that they don't care about
* the buffer size [we aren't programming in C here, after all] and just try
* to get the data. This will work if either the buffer already allocated for
* the SV [scalar value] is large enough to hold the data or the API provides
* an easy way to determine the required size [and the XS code uses it].
*
* If the user passes in a numeric value for a buffer size, then the XS
* code makes sure that the buffer is at least large enough to hold a value
* of that size and then passes in how large the buffer is. So the buffer
* size passed to the API call is the larger of the size requested by the
* user and the size of the buffer already allocated to the SV.
*
* The user can also pass in a string consisting of a leading "=" followed
* by digits for a buffer size. This means just use the size specified after
* the equals sign, even if the allocated buffer is larger. The XS code will
* still allocate a large enough buffer before the first call.
*
* If the function is nice enough to tell us that a buffer was too small
* [usually via ERROR_MORE_DATA] _and_ how large the buffer needs to be,
* then the XS code should enlarge the buffer(s) and repeat the call [once].
* This resizing is _not_ done for buffers whose size was specified with a
* leading "=".
*
* Only grow_buf() and perhaps trunc_buf() can be used in a typemap file.
* The other macros would be used in the parameter declarations or INPUT:
* section [grow_*()], the INIT: section [init_*()], or the OUTPUT: section
* [trunc_*()].
*
* Buffer arguments should be initialised with C<= NO_INIT> [or C<= NULL;>].
*
* See also the F<typemap> file. C<oDWORD>, for example, is for an output-
* only parameter of type C<DWORD> and you should simply C<#define> it to be
* C<DWORD>. In F<typemap>, C<oDWORD> is treated differently than C<DWORD>
* in two ways.
*
* First, if C<undef> is passed in, a C<DWORD> could generate a warning
* when it gets converted to 0 while C<oDWORD> will never generate such a
* warning for C<undef>. This first difference doesn't apply if specific
* initialization is specified for the variable, as in C<= init_buf_l($var);>.
* In particular, the init_*() macros also convert C<undef> to 0 without
* ever producing a warning.
*
* Second, passing in a read-only SV for a C<oDWORD> parameter will generate
* a fatal error on output when we try to update the SV. For C<DWORD>, we
* won't update a read-only SV since passing in a literal constant for a
* buffer size is a useful thing to do even though it prevents us from
* returning the size of data written via that SV. Since we should use a
* trunc_*() macro to output the actual data, the user should be able to
* determine the size of data written based on the size of the scalar we
* output anyway.
*
* This second difference doesn't apply unless the parameter is listed in
* the OUTPUT: section without specific output instructions. We define
* no macros for outputting buffer length parameters so be careful to use
* C<oDWORD> [for example] for them if and only if they are output-only.
*
* Note that C<oDWORD> is the same as C<DWORD> in that, if a defined value
* is passed in, it is used [and can generate a warning if the value is
* "not numeric"]. So although C<oDWORD> is for output-only parameters,
* we still initialize the C variable before calling the API. This is good
* in case the parameter isn't always strictly output-only due to upgrades,
* bugs, etc.
*
* Here is a made-up example that shows several cases:
*
* # Actual GetDataW() returns length of data written to ioswName, not bool.
* bool
* GetDataW( ioswName, ilwName, oswText, iolwText, opJunk, opRec, ilRec, olRec )
* WCHAR * ioswName = NO_INIT
* DWORD ilwName = NO_INIT
* WCHAR * oswText = NO_INIT
* DWORD &iolwText = init_buf_l($arg);
* void * opJunk = NO_INIT
* BYTE * opRec = NO_INIT
* DWORD ilRec = init_buf_l($arg);
* oDWORD &olRec
* PREINIT:
* DWORD olwName;
* INIT:
* grow_buf_lw( ioswName,ST(0), ilwName,ST(1) );
* grow_buf_lw( oswText,ST(2), iolwText,ST(3) );
* grow_buf_typ( opJunk,ST(4),void *, LONG_STRUCT_TYPEDEF );
* grow_buf_l( opRec,ST(5),BYTE *, ilRec,ST(6) );
* CODE:
* olwName= GetDataW( ioswName, ilwName, oswText, &iolwText,
* (LONG_STRUCT_TYPEDEF *)opJunk, opRec, &iolRec );
* if( 0 == olwName && ERROR_MORE_DATA == GetLastError()
* && ( autosize(ST(1)) || autosize(ST(3)) || autosize(ST(6)) ) ) {
* if( autosize(ST(1)) )
* grow_buf_lw( ioswName,ST(0), ilwName,ST(1) );
* if( autosize(ST(3)) )
* grow_buf_lw( oswText,ST(2), iolwText,ST(3) );
* if( autosize(ST(6)) )
* grow_buf_l( opRec,ST(5),BYTE *, iolRec,ST(6) );
* olwName= GetDataW( ioswName, ilwName, oswText, &iolwText,
* (LONG_STRUCT_TYPEDEF *)opJunk, opRec, &iolRec );
* }
* RETVAL= 0 != olwName;
* OUTPUT:
* RETVAL
* ioswName trunc_buf_lw( RETVAL, ioswName,ST(0), olwName );
* oswText trunc_buf_lw( RETVAL, oswText,ST(2), iolwText );
* iolwText
* opJunk trunc_buf_typ(RETVAL,opJunk,ST(4),LONG_STRUCT_TYPEDEF);
* opRec trunc_buf_l( RETVAL, opRec,ST(5), olRec );
* olRec
*
* The above example would be more complex and less efficient if we used
* C<DWORD * iolwText> in place of C<DWORD &iolwText>. The only possible
* advantage would be that C<NULL> would be passed in for C<iolwText> if
* _both_ C<$oswText> and C<$iolwText> were specified as C<[]>. The *_pl*()
* macros are defined [and C<DWORD *> specified in F<typemap>] so we can
* handle those cases but it is usually better to use the *_l*() macros
* instead by specifying C<&> instead of C<*>. Using C<&> instead of C<*>
* is usually better when dealing with scalars, even if they aren't buffer
* sizes. But you must use C<*> if it is important for that parameter to
* be able to pass C<NULL> to the underlying API.
*
* In Win32API::, we try to use C<*> for buffer sizes of optional buffers
* and C<&> for buffer sizes of required buffers.
*
* For parameters that are pointers to things other than buffers or buffer
* sizes, we use C<*> for "important" parameters [so that using C<[]>
* generates an error rather than fetching the value and just throwing it
* away], and for optional parameters [in case specifying C<NULL> is or
* becomes important]. Otherwise we use C<&> [for "unimportant" but
* required parameters] so the user can specify C<[]> if they don't care
* about it. The output handle of an "open" routine is "important".
*/
#ifndef Debug
# define Debug(list) /*Nothing*/
#endif
/*#ifndef CAST
*# ifdef __cplusplus
*# define CAST(type,expr) static_cast<type>(expr)
*# else*/
# define CAST(type,expr) (type)(expr)
/*# endif
*#endif*/
/* Is an argument C<[]>, meaning we should pass C<NULL>? */
#define null_arg(sv) ( SvROK(sv) && SVt_PVAV == SvTYPE(SvRV(sv)) \
&& -1 == av_len((AV*)SvRV(sv)) )
#define PV_or_null(sv) ( null_arg(sv) ? NULL : SvPV_nolen(sv) )
/* Minimum buffer size to use when no buffer existed: */
#define MIN_GROW_SIZE 128
#ifdef Debug
/* Used in Debug() messages to show which macro call is involved: */
#define string(arg) #arg
#endif
/* Simplify using SvGROW() for byte-sized buffers: */
#define lSvGROW(sv,n) SvGROW( sv, 0==(n) ? MIN_GROW_SIZE : (n)+1 )
/* Simplify using SvGROW() for WCHAR-sized buffers: */
#define lwSvGROW(sv,n) CAST( WCHAR *, \
SvGROW( sv, sizeof(WCHAR)*( 0==(n) ? MIN_GROW_SIZE : (n)+1 ) ) )
/* Whether the buffer size we got lets us change what buffer size we use: */
#define autosize(sv) (!( SvOK(sv) && ! SvROK(sv) \
&& SvPV_nolen(sv) && '=' == *SvPV_nolen(sv) ))
/* Get the IV/UV for a parameter that might be C<[]> or C<undef>: */
#define optIV(sv) ( null_arg(sv) ? 0 : !SvOK(sv) ? 0 : SvIV(sv) )
#define optUV(sv) ( null_arg(sv) ? 0 : !SvOK(sv) ? 0 : SvUV(sv) )
/* Allocate temporary storage that will automatically be freed later: */
#ifndef TempAlloc /* Can be C<#define>d to be C<_alloca>, for example */
# define TempAlloc( size ) sv_grow( sv_newmortal(), size )
#endif
/* Initialize a buffer size argument of type (DWORD *): */
#define init_buf_pl( plSize, svSize, tpSize ) STMT_START { \
if( null_arg(svSize) ) \
plSize= NULL; \
else { \
STRLEN n_a; \
*( plSize= CAST( tpSize, TempAlloc(sizeof(*plSize)) ) )= \
autosize(svSize) ? optUV(svSize) \
: strtoul( 1+SvPV(svSize,n_a), NULL, 10 ); \
} } STMT_END
/* In INPUT section put ": init_buf_pl($var,$arg,$type);" after var name. */
/* Initialize a buffer size argument of type DWORD: */
#define init_buf_l( svSize ) \
( null_arg(svSize) ? 0 : autosize(svSize) ? optUV(svSize) \
: strtoul( 1+SvPV_nolen(svSize), NULL, 10 ) )
/* In INPUT section put "= init_buf_l($arg);" after variable name. */
/* Lengths in WCHARs are initialized the same as lengths in bytes: */
#define init_buf_plw init_buf_pl
#define init_buf_lw init_buf_l
/* grow_buf_pl() and grow_buf_plw() are included so you can define
* parameters of type C<DWORD *>, for example. In practice, it is
* usually better to define such parameters as "DWORD &". */
/* Grow a buffer where we have a pointer to its size in bytes: */
#define grow_buf_pl( sBuf,svBuf,tpBuf, plSize,svSize,tpSize ) STMT_START { \
Debug(("grow_buf_pl( %s==0x%lX,[%s:%ld/%ld, %s==0x%lX:%ld,[%s )\n",\
string(sBuf),sBuf,strchr(string(svBuf),'('),SvPOK(svBuf)? \
SvCUR(svBuf):-1,SvPOK(svBuf)?SvLEN(svBuf):-1,string(plSize), \
plSize,plSize?*plSize:-1,strchr(string(svSize),'('))); \
if( null_arg(svBuf) ) { \
sBuf= NULL; \
} else { \
STRLEN n_a; \
if( NULL == plSize ) \
*( plSize= CAST(tpSize,TempAlloc(sizeof(*plSize))) )= 0;\
if( ! SvOK(svBuf) ) sv_setpvn(svBuf,"",0); \
(void) SvPV_force( svBuf, n_a ); \
sBuf= CAST( tpBuf, lSvGROW( svBuf, *plSize ) ); \
if( autosize(svSize) ) *plSize= SvLEN(svBuf) - 1; \
Debug(("more buf_pl( %s==0x%lX,[%s:%ld/%ld, %s==0x%lX:%ld,[%s )\n",\
string(sBuf),sBuf,strchr(string(svBuf),'('),SvPOK(svBuf)? \
SvCUR(svBuf):-1,SvPOK(svBuf)?SvLEN(svBuf):-1,string(plSize),\
plSize,plSize?*plSize:-1,strchr(string(svSize),'('))); \
} } STMT_END
/* Grow a buffer where we have a pointer to its size in WCHARs: */
#define grow_buf_plw( sBuf,svBuf, plwSize,svSize,tpSize ) STMT_START { \
if( null_arg(svBuf) ) { \
sBuf= NULL; \
} else { \
STRLEN n_a; \
if( NULL == plwSize ) \
*( plwSize= CAST(tpSize,TempAlloc(sizeof(*plwSize))) )= 0;\
if( ! SvOK(svBuf) ) sv_setpvn(svBuf,"",0); \
(void) SvPV_force( svBuf, n_a ); \
sBuf= lwSvGROW( svBuf, *plwSize ); \
if( autosize(svSize) ) \
*plwSize= SvLEN(svBuf)/sizeof(WCHAR) - 1; \
} } STMT_END
/* Grow a buffer where we have its size in bytes: */
#define grow_buf_l( sBuf,svBuf,tpBuf, lSize,svSize ) STMT_START { \
if( null_arg(svBuf) ) { \
sBuf= NULL; \
} else { \
STRLEN n_a; \
if( ! SvOK(svBuf) ) sv_setpvn(svBuf,"",0); \
(void) SvPV_force( svBuf, n_a ); \
sBuf= CAST( tpBuf, lSvGROW( svBuf, lSize ) ); \
if( autosize(svSize) ) lSize= SvLEN(svBuf) - 1; \
} } STMT_END
/* Grow a buffer where we have its size in WCHARs: */
#define grow_buf_lw( swBuf,svBuf, lwSize,svSize ) STMT_START { \
if( null_arg(svBuf) ) { \
swBuf= NULL; \
} else { \
STRLEN n_a; \
if( ! SvOK(svBuf) ) sv_setpvn(svBuf,"",0); \
(void) SvPV_force( svBuf, n_a ); \
swBuf= lwSvGROW( svBuf, lwSize ); \
if( autosize(svSize) ) \
lwSize= SvLEN(svBuf)/sizeof(WCHAR) - 1; \
} } STMT_END
/* Grow a buffer that contains the declared fixed data type: */
#define grow_buf( pBuf,svBuf, tpBuf ) STMT_START { \
if( null_arg(svBuf) ) { \
pBuf= NULL; \
} else { \
STRLEN n_a; \
if( ! SvOK(svBuf) ) sv_setpvn(svBuf,"",0); \
(void) SvPV_force( svBuf, n_a ); \
pBuf= CAST( tpBuf, SvGROW( svBuf, sizeof(*pBuf) ) ); \
} } STMT_END
/* Grow a buffer that contains a fixed data type other than that declared: */
#define grow_buf_typ( pBuf,svBuf,tpBuf, Type ) STMT_START { \
if( null_arg(svBuf) ) { \
pBuf= NULL; \
} else { \
STRLEN n_a; \
if( ! SvOK(svBuf) ) sv_setpvn(svBuf,"",0); \
(void) SvPV_force( svBuf, n_a ); \
pBuf= CAST( tpBuf, SvGROW( svBuf, sizeof(Type) ) ); \
} } STMT_END
/* Grow a buffer that contains a list of items of the declared data type: */
#define grow_vect( pBuf,svBuf,tpBuf, cItems ) STMT_START { \
if( null_arg(svBuf) ) { \
pBuf= NULL; \
} else { \
STRLEN n_a; \
if( ! SvOK(svBuf) ) sv_setpvn(svBuf,"",0); \
(void) SvPV_force( svBuf, n_a ); \
pBuf= CAST( tpBuf, SvGROW( svBuf, sizeof(*pBuf)*cItems ) ); \
} } STMT_END
/* If call succeeded, set data length to returned length (in bytes): */
#define trunc_buf_l( bOkay, sBuf,svBuf, lSize ) STMT_START { \
if( bOkay && NULL != sBuf ) { \
SvPOK_only( svBuf ); \
SvCUR_set( svBuf, lSize ); \
} } STMT_END
/* Same as above except we have a pointer to the returned length: */
#define trunc_buf_pl( bOkay, sBuf,svBuf, plSize ) \
trunc_buf_l( bOkay, sBuf,svBuf, *plSize )
/* If call succeeded, set data length to returned length (in WCHARs): */
#define trunc_buf_lw( bOkay, sBuf,svBuf, lwSize ) STMT_START { \
if( bOkay && NULL != sBuf ) { \
SvPOK_only( svBuf ); \
SvCUR_set( svBuf, (lwSize)*sizeof(WCHAR) ); \
} } STMT_END
/* Same as above except we have a pointer to the returned length: */
#define trunc_buf_plw( bOkay, swBuf,svBuf, plwSize ) \
trunc_buf_lw( bOkay, swBuf,svBuf, *plwSize )
/* Set data length for a buffer that contains the declared fixed data type: */
#define trunc_buf( bOkay, pBuf,svBuf ) STMT_START { \
if( bOkay && NULL != pBuf ) { \
SvPOK_only( svBuf ); \
SvCUR_set( svBuf, sizeof(*pBuf) ); \
} } STMT_END
/* Set data length for a buffer that contains some other fixed data type: */
#define trunc_buf_typ( bOkay, pBuf,svBuf, Type ) STMT_START { \
if( bOkay && NULL != pBuf ) { \
SvPOK_only( svBuf ); \
SvCUR_set( svBuf, sizeof(Type) ); \
} } STMT_END
/* Set length for buffer that contains list of items of the declared type: */
#define trunc_vect( bOkay, pBuf,svBuf, cItems ) STMT_START { \
if( bOkay && NULL != pBuf ) { \
SvPOK_only( svBuf ); \
SvCUR_set( svBuf, sizeof(*pBuf)*cItems ); \
} } STMT_END
/* Set data length for a buffer where a '\0'-terminate string was stored: */
#define trunc_buf_z( bOkay, sBuf,svBuf ) STMT_START { \
if( bOkay && NULL != sBuf ) { \
SvPOK_only( svBuf ); \
SvCUR_set( svBuf, strlen(sBuf) ); \
} } STMT_END
/* Set data length for a buffer where a L'\0'-terminate string was stored: */
#define trunc_buf_zw( bOkay, sBuf,svBuf ) STMT_START { \
if( bOkay && NULL != sBuf ) { \
SvPOK_only( svBuf ); \
SvCUR_set( svBuf, wcslen(sBuf)*sizeof(WCHAR) ); \
} } STMT_END