[gambit-list] Is ___CAST(void*, ___BODY(u8v)) the optimal way to resolve u8vector's data in C?

Marc Feeley feeley at iro.umontreal.ca
Thu Oct 29 12:25:17 EDT 2020

The various options do slightly different things, based on different assumptions.  Say that “obj” contains a reference (of type ___SCMOBJ) to a memory allocated Scheme object, possibly a u8vector, vector, pair etc, but not a fixnum, #f/#t, character, etc (which have all their information embedded in the 32 or 64 bit reference itself).  Then

___BODY_AS(obj,___tPAIR) returns a pointer of type ___SCMOBJ* to the first 32 or 64 bit word of the pair “obj” (which is normally the cdr field of the pair). “obj” must be a reference to a pair.

___BODY_AS(obj,___tSUBTYPED) returns a pointer of type ___SCMOBJ* to the first 32 or 64 bit word of the subtyped “obj”.  “obj” must be a reference to a subtyped object, like a u8vector or vector.

___BODY(obj) returns a pointer of type ___SCMOBJ* to the first 32 or 64 bit word of the object’s content regardless of it being a pair or subtyped object.

The reason for the two APIs is that ___BODY(obj) is slightly slower than ___BODY_AS(obj,…).  ___BODY_AS(obj,…) only needs to add an offset to “obj”, but ___BODY(obj) must mask the tag bits and then add an offset.  In typical code the difference is so small that it is hard to measure.  However, when the compiler knows the type of “obj” it will use the faster form.  For example the ___CAR(…) macro is essentially implemented with:

#define ___PAIR_CAR 1
#define ___CAR(obj)___BODY_AS(obj,___tPAIR)[___PAIR_CAR]

So one offset is added to obj by the ___BODY_AS, and another for the indexing (a pattern that typical C compilers will turn into a single combined offset at the machine instruction level).  If ___BODY had been used, the C compiler can’t combine the masking and the offset in one machine instruction.

And for the form ___FETCH_U8:

___FETCH_U8(obj,___INT(N)) returns the ___U8 value at the Nth byte of the body of “obj” (using 0 based indexing).  The type ___U8 is normally equivalent to “unsigned char”.  There’s also ___FETCH_U16, ___FETCH_U32, etc.  This set of macros allows an abstraction of arrays of ___U8, ___U16, etc values for portability to machines that aren’t byte-addressable (very rare these days!).

So I think you are better off defining your own abstraction:

#define U8VECTOR_BODY(obj) ___CAST(___U8*,___BODY_AS(obj,___tSUBTYPED))


> On Oct 29, 2020, at 10:51 AM, Phillip Suero <philsuero at gmail.com> wrote:
> Hi!
> In C, I like to get the memory address (void*, char*, etc.) of the blob in a Scheme u8vector. That is a u8vector that is _still_, of course. Also of course this means a pointer to the first byte in the u8vector.
> I see how to do it here http://gambitscheme.org/wiki/index.php/Using_Gambit_with_External_Libraries#Accessing_Scheme_vectors_within_a_C_function , and there are multiple suggestions in there.
> Of all the suggestions, the "void *u8vectorptr = ___CAST(void*,___BODY(u8v));" form is the shortest form.
> The default recommended form however uses "___BODY_AS(u8v,___tSUBTYPED)" instead of ___BODY. Another suggestion uses &___FETCH_U8(___arg1,0), or instead of 0, ___INT(0).
> Can you please explain, is there any material difference or advantage between these?
> Are they equal so all else the same, the shortest form I mentioned is the preferable one?
> Thanks,
> Phil
> _______________________________________________
> Gambit-list mailing list
> Gambit-list at iro.umontreal.ca
> https://mailman.iro.umontreal.ca/cgi-bin/mailman/listinfo/gambit-list

More information about the Gambit-list mailing list