Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bitfields #1616

Merged
merged 1 commit into from
Sep 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/ref/integers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -174,5 +174,20 @@ random integers and random choices from lists.
<#Include Label="RandomSource">

</Section>
<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->
<Section Label="Bitfields">
<Heading>Bitfields</Heading>
Bitfields are a low-level feature intended to support efficient
subdivision of immediate integers into bitfields of various widths.
This is typically useful in implementing space-efficient and/or
cache-efficient data structures. This feature should be used with care
because (<E>inter alia</E>) it has different limitations on 32-bit and 64-bit
architectures.

<#Include Label="MakeBitfields">
<#Include Label="BuildBitfields">

</Section>

</Chapter>

1 change: 1 addition & 0 deletions doc/ref/makedocreldata.g
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ GAPInfo.ManualDataRef:= rec(
"../../lib/attr.gd",
"../../lib/basis.gd",
"../../lib/basismut.gd",
"../../lib/bitfields.gd",
"../../lib/boolean.g",
"../../lib/clas.gd",
"../../lib/cmdledit.g",
Expand Down
119 changes: 119 additions & 0 deletions lib/bitfields.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#############################################################################
##
#W bitfields.gd GAP library Steve Linton
##
##
#Y Copyright (C) 2017 The GAP Group
##
## This file declares the operations for bitfields
##


#############################################################################
##
#F MakeBitfields(<width>[, <width>[, <width>...]]])
## <#GAPDoc Label="MakeBitfields">
## <ManSection>
## <Func Name="MakeBitfields" Arg='width....'/>
##
## <Description>
##
## This function sets up the machinery for a set of bitfields of the given
## widths. All bitfield values are treated as unsigned.
## The total of the widths must not exceed 60 bits on 64-bit architecture
## or 28 bits on a 32-bit architecture. For performance
## reasons some checks that one might wish to do are ommitted. In particular,
## the builder and setter functions do not check if the value[s] passed to them are
## negative or too large (unless &GAP; is specially compiled for debugging).
## Behaviour when such arguments are passed is undefined.
##
## You can tell which type of architecture you are running on by acccessing
## <C>GAPInfo.BytesPerVariable</C> which is 8 on 64-bits and 4 on 32.
##
##
## The return value when <M>n</M> widths are given is a record whose fields are
## <List>
## <Mark><C>widths</C></Mark> <Item>a copy of the arguments, for convenience,</Item>
## <Mark><C>getters</C></Mark> <Item> a list of <M>n</M> functions of one
## argument each of which extracts one of the fields from an immediate integer</Item>
## <Mark><C>setters</C></Mark> <Item> a list of <M>n</M> functions each taking
## two arguments: a packed value and a new value for one of its fields and
## returning a new packed value. The
## <M>i</M>th function returned the new packed value in which the <M>i</M>th
## field has been replaced by the new value.
## Note that this does NOT modify the original packed value.</Item>
##
## </List>
##
## Two additional fields may be present if any of the field widths is
## one. Each is a list and only has entried bound in the positions
## corresponding to the width 1 fields.
## <List> <Mark><C>booleanGetters</C></Mark> <Item>if the <M>i</M>th position of
## this list is set, it contains a function which extracts the <M>i</M>th
## field (which will have width one) and returns <C>true</C> if it contains 1
## and <C>false</C> if it contains 0</Item>
## <Mark><C>booleanSetters</C></Mark> <Item>if the <M>i</M>th position of
## this list is set, it contains a function of two arguments. The first
## argument is a packed value, the second is <C>true</C> or <C>false</C>. It returns a
## new packed value in which the <M>i</M>th field is set to 1 if the second
## argument was <C>true</C> and 0 if it was <C>false</C>. Behaviour for any
## other value is undefined.</Item></List>
## </Description>
## </ManSection>
## <#/GAPDoc>

DeclareGlobalFunction( "MakeBitfields" );

##
#############################################################################
##
#F BuildBitfields( <widths>[, <val1>, [<val2>...]])
## <#GAPDoc Label="BuildBitfields">
## <ManSection>
## <Func Name="BuildBitfields" Arg='widths,....'/>
## <Description>
##
## This function takes one or more argument. It's first argument is a list
## of field widths, as found in the <C>widths</C> entry of a record returned
## by <C>MakeBitfields</C>. The remaining arguments are unsigned integer
## values, equal in number to the entries of the list of field widths. It returns
## a small integer in which those entries are packed into bitfields of the
## given widths. The first entry occupies the least significant bits.
##
##

DeclareGlobalFunction("BuildBitfields");

##
## <Example><![CDATA[
## gap> bf := MakeBitfields(1,2,3);
## rec( booleanGetters := [ function( data ) ... end ],
## booleanSetters := [ function( data, val ) ... end ],
## getters := [ function( data ) ... end, function( data ) ... end,
## function( data ) ... end ],
## setters := [ function( data, val ) ... end, function( data, val ) ... end,
## function( data, val ) ... end ], widths := [ 1, 2, 3 ] )
## gap> x := BuildBitfields(bf.widths,0,3,5);
## 46
## gap> bf.getters[3](x);
## 5
## gap> y := bf.setters[1](x,1);
## 47
## gap> x;
## 46
## gap> bf.booleanGetters[1](x);
## false
## gap> bf.booleanGetters[1](y);
## true
## ]]></Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
##



#############################################################################
##
#E

2 changes: 2 additions & 0 deletions lib/bitfields.gi
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
InstallGlobalFunction(MakeBitfields, MAKE_BITFIELDS);
InstallGlobalFunction(BuildBitfields, BUILD_BITFIELDS);
1 change: 1 addition & 0 deletions lib/read3.g
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ ReadLib( "extrset.gd" );
ReadLib( "extuset.gd" );

ReadLib( "dict.gd" );
ReadLib( "bitfields.gd" );

ReadLib( "mapping.gd" );
ReadLib( "mapphomo.gd" );
Expand Down
1 change: 1 addition & 0 deletions lib/read5.g
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ ReadLib( "grppcatr.gi" );
ReadLib( "grppcnrm.gi" );

# files dealing with trees and hash tables
ReadLib( "bitfields.gi" );
ReadLib( "dict.gi" );
ReadLib( "dicthf.gi" );

Expand Down
207 changes: 207 additions & 0 deletions src/intfuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,207 @@ Obj FuncINT_STRING ( Obj self, Obj string )
return IntStringInternal(string);
}

/****************************************************************************
**
*F SmallInt Bitfield operations
*
* The goal here it to set up a division of the usable bits in a small
* integer into fields which can be accessed very quickly from GAP
* level and quickly and conveniently from C. The purpose is to allow
* implementation of data structures that divide up the bits within a
* word without having to make them entirely opaque to the GAP level or
* ridiculously slow
*
* The API is defined in lib/bitfields.gd and works by providing the
* user with a collection of functions to get and set fields and
* assemble an entire word.
*
* These functions are constructed here and have special handlers. The
* information the handlers need about the size and position of the
* bitfields are stored in some of the fields of the function header
* which are not normally used for kernel functions. Specifically, we
* use the NLOC_FUNC and FEXS_FUNC fields, which we alias as
* MASK_BITFIELD_FUNC and OFFSET_BITFIELD_FUNC.
*
* For fields of size 1 we also offer Boolean setters and getters which
* accept and return True for 1 and False for 0. This makes for much
* nicer code on the GAP side.
*
*/


static inline UInt MASK_BITFIELD_FUNC(Obj func)
{
return NLOC_FUNC(func);
}

static inline void SET_MASK_BITFIELD_FUNC(Obj func, UInt mask)
{
SET_NLOC_FUNC(func, mask);
}

static inline UInt OFFSET_BITFIELD_FUNC(Obj func)
{
GAP_ASSERT(IS_INTOBJ(FEXS_FUNC(func)));
return INT_INTOBJ(FEXS_FUNC(func));
}

static inline void SET_OFFFSET_BITFIELD_FUNC(Obj func, UInt offset)
{
return SET_FEXS_FUNC(func, INTOBJ_INT(offset));
}

static Obj DoFieldGetter(Obj self, Obj data)
{
UInt mask = MASK_BITFIELD_FUNC(self);
UInt offset = OFFSET_BITFIELD_FUNC(self);
if (!IS_INTOBJ(data))
ErrorMayQuit("Field getter: argument must be small integer", 0, 0);
UInt x = INT_INTOBJ(data);
return INTOBJ_INT((x & mask) >> offset);
}

static Obj DoFieldSetter(Obj self, Obj data, Obj val)
{
UInt mask = MASK_BITFIELD_FUNC(self);
UInt offset = OFFSET_BITFIELD_FUNC(self);
if (!ARE_INTOBJS(data, val))
ErrorMayQuit("Field Setter: both arguments must be small integers", 0,
0);
UInt x = INT_INTOBJ(data);
UInt y = INT_INTOBJ(val);
return INTOBJ_INT((x & ~mask) | (y << offset));
}

static Obj DoBooleanFieldGetter(Obj self, Obj data)
{
UInt mask = MASK_BITFIELD_FUNC(self);
if (!IS_INTOBJ(data))
ErrorMayQuit("Boolean Field getter: argument must be small integer", 0, 0);
UInt x = INT_INTOBJ(data);
return (x & mask) ? True : False;
}

static Obj DoBooleanFieldSetter(Obj self, Obj data, Obj val)
{
UInt mask = MASK_BITFIELD_FUNC(self);
if (!IS_INTOBJ(data))
ErrorMayQuit("Boolean Field Setter: data must be small integer", 0,
0);
UInt x = INT_INTOBJ(data);
if (val == True)
x |= mask;
else if (val == False)
x &= ~mask;
else
ErrorMayQuit("Boolean Field Setter: value must be true or false", 0,
0);
return INTOBJ_INT(x);
}


static Obj FuncBUILD_BITFIELDS(Obj self, Obj args)
{
GAP_ASSERT(IS_PLIST(args));
GAP_ASSERT(LEN_PLIST(args) >= 1 && ELM_PLIST(args, 1));
Obj widths = ELM_PLIST(args, 1);
if (!IS_LIST(widths))
ErrorMayQuit("Fields builder: first argument must be list of widths",
0, 0);
UInt nfields = LEN_LIST(widths);
if (LEN_PLIST(args) != nfields + 1)
ErrorMayQuit(
"Fields builder: number of values must match number of widths", 0,
0);
UInt x = 0;
UInt i;
for (i = nfields; i > 0; i--) {
GAP_ASSERT(ISB_LIST(widths, i));
Obj y = ELM_LIST(widths, i);
x <<= INT_INTOBJ(y);
GAP_ASSERT(ELM_PLIST(args, i + 1));
Obj z = ELM_PLIST(args, i + 1);
if (!IS_INTOBJ(z))
ErrorMayQuit("Fields builder: values must be small integers", 0,
0);
GAP_ASSERT(INT_INTOBJ(z) < (1 << INT_INTOBJ(y)));
x |= INT_INTOBJ(z);
}
return INTOBJ_INT(x);
}


Obj FuncMAKE_BITFIELDS(Obj self, Obj widths)
{
if (!IS_LIST(widths))
ErrorMayQuit("MAKE_BITFIELDS: widths must be a list", 0, 0);
UInt nfields = LEN_LIST(widths);
UInt starts[nfields + 1];
starts[0] = 0;
for (UInt i = 1; i <= nfields; i++) {
Obj o = ELM_LIST(widths, i);
if (!IS_INTOBJ(o))
ErrorMayQuit("MAKE_BITFIELDS: widths must be small integers", 0,
0);
UInt width = INT_INTOBJ(o);
starts[i] = starts[i - 1] + width;
}
if (starts[nfields] > 8 * sizeof(UInt))
ErrorMayQuit("MAKE_BITFIELDS: total widths too large", 0, 0);

Obj setters = NEW_PLIST(T_PLIST_DENSE + IMMUTABLE, nfields);
Obj getters = NEW_PLIST(T_PLIST_DENSE + IMMUTABLE, nfields);
Obj bsetters = NEW_PLIST(T_PLIST + IMMUTABLE, nfields);
UInt bslen = 0;
Obj bgetters = NEW_PLIST(T_PLIST + IMMUTABLE, nfields);
for (UInt i = 1; i <= nfields; i++) {
UInt mask = (1L << starts[i]) - (1L << starts[i - 1]);
Obj s = NewFunctionC("<field setter>", 2, "data, val", DoFieldSetter);
SET_MASK_BITFIELD_FUNC(s, mask);
SET_OFFFSET_BITFIELD_FUNC(s, starts[i - 1]);
SET_ELM_PLIST(setters, i, s);
CHANGED_BAG(setters);
Obj g = NewFunctionC("<field getter>", 1, "data", DoFieldGetter);
SET_MASK_BITFIELD_FUNC(g, mask);
SET_OFFFSET_BITFIELD_FUNC(g, starts[i - 1]);
SET_ELM_PLIST(getters, i, g);
CHANGED_BAG(getters);
if (starts[i] - starts[i - 1] == 1) {
s = NewFunctionC("<boolean field setter>", 2, "data, val",
DoBooleanFieldSetter);
SET_MASK_BITFIELD_FUNC(s, mask);
SET_OFFFSET_BITFIELD_FUNC(s, starts[i - 1]);
SET_ELM_PLIST(bsetters, i, s);
CHANGED_BAG(bsetters);
bslen = i;
g = NewFunctionC("<boolean field getter>", 1, "data",
DoBooleanFieldGetter);
SET_MASK_BITFIELD_FUNC(g, mask);
SET_OFFFSET_BITFIELD_FUNC(g, starts[i - 1]);
SET_ELM_PLIST(bgetters, i, g);
CHANGED_BAG(bgetters);
}
}

SET_LEN_PLIST(setters, nfields);
SET_LEN_PLIST(getters, nfields);
SET_LEN_PLIST(bsetters, bslen);
SET_LEN_PLIST(bgetters, bslen);

Obj ms = NEW_PREC(5);
AssPRec(ms, RNamName("widths"), CopyObj(widths, 0));
AssPRec(ms, RNamName("getters"), getters);
AssPRec(ms, RNamName("setters"), setters);
if (bslen > 0) {
AssPRec(ms, RNamName("booleanGetters"), bgetters);
AssPRec(ms, RNamName("booleanSetters"), bsetters);
}
SortPRecRNam(ms, 0);
RetypeBag(ms, T_PREC + IMMUTABLE);
return ms;
}


/****************************************************************************
**
*F * * * * * * * * * * * * * initialize package * * * * * * * * * * * * * * *
Expand All @@ -632,6 +833,8 @@ static StructGVarFunc GVarFuncs [] = {
GVAR_FUNC(SIZE_OBJ, 1, "obj"),
GVAR_FUNC(InitRandomMT, 1, "initstr"),
GVAR_FUNC(INT_STRING, 1, "string"),
GVAR_FUNC(MAKE_BITFIELDS, -1, "widths"),
GVAR_FUNC(BUILD_BITFIELDS, -2, "widths..."),
{ 0, 0, 0, 0, 0 }

};
Expand All @@ -645,6 +848,10 @@ static Int InitKernel (
StructInitInfo * module )
{

InitHandlerFunc(DoFieldSetter, "field-setter");
InitHandlerFunc(DoFieldGetter, "field-getter");
InitHandlerFunc(DoBooleanFieldSetter, "boolean-field-setter");
InitHandlerFunc(DoBooleanFieldGetter, "boolean-field-getter");

/* init filters and functions */
InitHdlrFuncsFromTable( GVarFuncs );
Expand Down
Loading