]> granicus.if.org Git - postgresql/commitdiff
Attached are the C-routines that implement a BIT and BIT VARYING type.
authorBruce Momjian <bruce@momjian.us>
Mon, 29 Nov 1999 22:34:36 +0000 (22:34 +0000)
committerBruce Momjian <bruce@momjian.us>
Mon, 29 Nov 1999 22:34:36 +0000 (22:34 +0000)
Adriaan Joubert

contrib/bit/Makefile [new file with mode: 0644]
contrib/bit/README [new file with mode: 0644]
contrib/bit/postgres.h [new file with mode: 0644]
contrib/bit/varbit.c [new file with mode: 0644]
contrib/bit/varbit.h [new file with mode: 0644]
contrib/bit/vartest.c [new file with mode: 0644]

diff --git a/contrib/bit/Makefile b/contrib/bit/Makefile
new file mode 100644 (file)
index 0000000..9603cba
--- /dev/null
@@ -0,0 +1,10 @@
+CFLAGS = -g
+
+varbit: vartest.o varbit.o
+       $(CC) $(CFLAGS) -o $@ $^
+
+varbit.o: varbit.c varbit.h
+vartest.o: vartest.c varbit.h
+
+clean:
+       rm -f *.o varbit
diff --git a/contrib/bit/README b/contrib/bit/README
new file mode 100644 (file)
index 0000000..b2ddb90
--- /dev/null
@@ -0,0 +1,77 @@
+A set of C routines to implement an SQL-compliant bitstring type.
+
+The file varbit.c contains the c-functions to implement both BIT and
+BIT VARYING. Both types are implemented in essentially the same way,
+except that BIT is zero padded to a specified length. I've tried to
+make this code as independent as possible of the byte length, but it
+is quite possible that there may be problems on machines that don't
+have 8 bits/byte (are there still any around?).
+
+In the input routines I have assumed that the parser eats the quotes
+in B'...' or X'...'.
+
+The SQL standard only defines comparison, SUBSTR and concatenation
+operators, and these have been implemented. In addition all logical
+operators have been implemented, i.e. ~,|,&,^,<< and >>. This is
+useful if one wants to build bit masks. If the two strings are not of
+the same length the longer string is truncated (truncation was the
+only real option, as padding with zeros could give unintuitive results
+for ^) and the result has the length of the shorter string. If there
+is a requirement for any other functions, let me know, and I will have
+a look.
+
+My knowledge of postgres is not up to integrating a type, so I'm hoping
+that somebody can integrate this type for me, or give me some hints as
+to what needs to be done. These routines were developed outside the
+postgres source tree, with a hacked version of postgres.h. The header
+files probably need some ammending.
+
+The included files are 
+
+varbit.h       -- bit string header type
+varbit.c       -- the routines
+vartest.c      -- a few calls to the routines to 
+
+The following routines are available. 
+
+char * zpbitin(char *s, int dummy,  int32 atttypmod);
+       Read in a zero padded bit string of the form X'...' or B'...'
+    
+char * zpbitout(char *s);
+       Print a zero padded bit string in hex X'...'
+
+char * zpbitsout(char *s);
+       Print a zero padded bit string in binary B'...'
+
+char * varbitin(char *s, int dummy,  int32 atttypmod);
+       Read in a varying length bit string of the form X'...' or B'...'
+
+[There is no need for separate output functions for varying bit, as 
+ zpbitout will print them out correctly]
+
+char * bitcat (char *arg1, char *arg2);
+       Bit concatenation. 
+
+char * bitsubstr (char *arg, int32 s, int32 l);
+       Substring of a bit string.
+
+bool biteq (char *arg1, char *arg2);
+bool bitne (char *arg1, char *arg2);
+bool bitge (char *arg1, char *arg2);
+bool bitgt (char *arg1, char *arg2);
+bool bitle (char *arg1, char *arg2);
+bool bitlt (char *arg1, char *arg2);
+int bitcmp (char *arg1, char *arg2);
+       Comparison operators
+
+char * bitand (char * arg1, char * arg2);
+char * bitor (char * arg1, char * arg2);
+char * bitxor (char * arg1, char * arg2);
+char * bitnot (char * arg);
+char * bitshiftright (char * arg, int shft);
+char * bitshiftleft (char * arg, int shft);
+       Bit operations.
+
+If anything else needs to be done, please let me know.
+
+Adriaan (adriaan@albourne.com)
diff --git a/contrib/bit/postgres.h b/contrib/bit/postgres.h
new file mode 100644 (file)
index 0000000..44da5d7
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef POSTGRES_H
+#define POSTGRES_H
+
+#include <stdio.h>
+
+typedef char bool;
+typedef signed char int8;
+typedef signed short int16;
+typedef signed int int32;
+
+/*#define NULL ((void *) 0)*/
+#define Min(x, y)           ((x) < (y) ? (x) : (y))
+#define Max(x, y)           ((x) > (y) ? (x) : (y))
+#define PointerIsValid(pointer) (bool)((void*)(pointer) != NULL)
+
+
+typedef unsigned int Oid;
+typedef int16 int2;
+typedef int32 int4;
+typedef float float4;
+typedef double float8;
+typedef unsigned char uint8;    /* == 8 bits */
+typedef unsigned short uint16;  /* == 16 bits */
+typedef unsigned int uint32;    /* == 32 bits */
+typedef uint8 bits8;                    /* >= 8 bits */
+typedef uint16 bits16;                  /* >= 16 bits */
+typedef uint32 bits32;                  /* >= 32 bits */
+
+
+typedef int4 aclitem;
+
+#define InvalidOid             0
+#define OidIsValid(objectId)  ((bool) (objectId != InvalidOid))
+
+/* unfortunately, both regproc and RegProcedure are used */
+typedef Oid regproc;
+typedef Oid RegProcedure;
+
+typedef char *((*func_ptr) ());
+
+
+#define RegProcedureIsValid(p) OidIsValid(p)
+
+/* ----------------------------------------------------------------
+ *                             Section 2:      variable length and array types
+ * ----------------------------------------------------------------
+ */
+/* ----------------
+ *             struct varlena
+ * ----------------
+ */
+struct varlena
+{
+       int32           vl_len;
+       char            vl_dat[1];
+};
+
+#define VARSIZE(PTR)   (((struct varlena *)(PTR))->vl_len)
+#define VARDATA(PTR)   (((struct varlena *)(PTR))->vl_dat)
+#define VARHDRSZ               sizeof(int32)
+
+typedef struct varlena bytea;
+typedef struct varlena text;
+
+typedef int2 int28[8];
+typedef Oid oid8[8];
+
+#define ERROR stderr
+#define elog fprintf
+
+#define MaxAttrSize 10000
+
+#define palloc malloc
+#endif
diff --git a/contrib/bit/varbit.c b/contrib/bit/varbit.c
new file mode 100644 (file)
index 0000000..257f976
--- /dev/null
@@ -0,0 +1,832 @@
+/*-------------------------------------------------------------------------
+ *
+ * varbit.c
+ *       Functions for the built-in type bit() and varying bit().
+ *
+ * IDENTIFICATION
+ *       $Header: /cvsroot/pgsql/contrib/bit/Attic/varbit.c,v 1.1 1999/11/29 22:34:36 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "varbit.h"
+/*
+#include "access/htup.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+*/
+
+
+
+/* 
+   Prefixes:
+     zp    -- zero-padded fixed length bit string
+     var   -- varying bit string
+
+   attypmod -- contains the length of the bit string in bits, or for
+               varying bits the maximum length. 
+
+   The data structure contains the following elements:
+      header  -- length of the whole data structure (incl header)
+                 in bytes. (as with all varying length datatypes)
+      data section -- private data section for the bits data structures
+         bitlength -- lenght of the bit string in bits
+        bitdata   -- least significant byte first string
+*/
+
+/*
+ * zpbitin -
+
+ *       converts a string to the internal representation of a bitstring.
+ *        The length is determined by the number of bits required plus
+ *        VARHDRSZ bytes or from atttypmod. 
+ *       (XXX dummy is here because we pass typelem as the second argument 
+ *        for array_in. copied this, no idea what it means??)
+ */
+char *
+zpbitin(char *s, int dummy,  int32 atttypmod)
+{
+  char *result, 
+    *sp;               /* pointer into the character string  */
+  bits8 *r;
+  int len,             /* Length of the whole data structure */
+    bitlen,            /* Number of bits in the bit string   */
+    slen;              /* Length of the input string         */
+  int bit_not_hex;      /* 0 = hex string  1=bit string       */
+  int i, bc, ipad;
+  bits8 x, y;
+
+
+  if (s == NULL)
+    return NULL;
+
+  /* Check that the first character is a b or an x */
+  if (s[0]=='b' || s[0]=='B') 
+      bit_not_hex = 1;
+  else if (s[0]=='x' || s[0]=='X') 
+      bit_not_hex = 0;
+  else 
+    elog(ERROR, "zpbitin: %s is not a valid bitstring",s);
+
+  slen = strlen(s) - 1;
+  /* Determine bitlength from input string */
+  bitlen = slen;
+  if (!bit_not_hex)
+    bitlen *= 4;
+  
+  /* Sometimes atttypmod is not supplied. If it is supplied we need to make
+     sure that the bitstring fits. Note that the number of infered bits can
+     be larger than the number of actual bits needed, but only if we are 
+     reading a hex string and not by more than 3 bits, as a hex string gives 
+     and accurate length upto 4 bits */
+  if (atttypmod == -1)
+    atttypmod = bitlen;
+  else
+    if (bitlen>atttypmod && bit_not_hex || bitlen>atttypmod+3 && !bit_not_hex)
+         elog(ERROR, "zpbitin: bit string of size %d cannot be written into bits(%d)",
+              bitlen,atttypmod);
+
+
+  len = VARBITDATALEN(atttypmod);
+
+  if (len > MaxAttrSize)
+    elog(ERROR, "zpbitin: length of bit() must be less than %d",
+        (MaxAttrSize-VARHDRSZ-VARBITHDRSZ)*BITSPERBYTE);
+
+  result = (char *) palloc(len);
+  /* set to 0 so that *r is always initialised and strin is zero-padded */
+  memset(result, 0, len);
+  VARSIZE(result) = len;
+  VARBITLEN(result) = atttypmod;
+
+  /* We need to read the bitstring from the end, as we store it least 
+     significant byte first. s points to the byte before the beginning
+     of the bitstring */
+  sp = s+1;
+  r = (bits8 *) VARBITS(result);
+  if (bit_not_hex) 
+    {
+      /* Parse the bit representation of the string */
+      /* We know it fits, as bitlen was compared to atttypmod */
+      x = BITHIGH;
+      for (bc = 0; sp != s+slen+1; sp++, bc++)
+       {
+         if (*sp=='1')
+           *r |= x;
+         if (bc==7) {
+           bc = 0;
+           x = BITHIGH;
+           r++;
+         } else 
+           x >>= 1;
+       }
+     }
+  else 
+    {
+      /* Parse the hex representation of the string */
+      for (bc = 0; sp != s+slen+1; sp++)
+       {
+         if (*sp>='0' && *sp<='9') 
+           x = (bits8) (*sp - '0');
+         else if (*sp>='A' && *sp<='F') 
+           x = (bits8) (*sp - 'A') + 10;
+         else if (*sp>='a' && *sp<='f') 
+           x = (bits8) (*sp - 'a') + 10;
+         else 
+           elog(ERROR,"Cannot parse %c as a hex digit",*sp);
+         if (bc) {
+           bc = 0;
+           *r++ |= x;
+         } else {
+           bc++;
+           *r = x<<4;
+         }
+       }
+    }
+
+  if (bitlen > atttypmod) {
+    /* Check that this fitted */
+    r = (bits8 *) (result + len - 1);
+    ipad = VARBITPAD(result);
+    /* The bottom ipad bits of the byte pointed to by r need to be zero */
+    /*    printf("Byte %X  shift %X %d\n",*r,(*r << (8-ipad)) & BITMASK,
+          (*r << (8-ipad)) & BITMASK > 0);
+    */
+    if (((*r << (BITSPERBYTE-ipad)) & BITMASK) > 0)
+      elog(ERROR, "zpbitin: bit string too large for bit(%d) data type",
+          atttypmod);
+  }
+
+  return result;
+}
+
+/* zpbitout -
+ *    for the time being we print everything as hex strings, as this is likely 
+ *    to be more compact than bit strings, and consequently much more efficient
+ *    for long strings
+ */
+char *
+zpbitout(char *s)
+{
+  char    *result, *r;
+  VarBit   sp;
+  int     i, len, bitlen;
+  
+  if (s == NULL)
+    {
+      result = (char *) palloc(2);
+      result[0] = '-';
+      result[1] = '\0';
+    }
+  else
+    {
+      bitlen = VARBITLEN(s);
+      len = bitlen/4 + (bitlen%4>0 ? 1 : 0);
+      result = (char *) palloc(len + 4);
+      sp = (bits8 *) VARBITS(s);
+      r = result;
+      *r++ = 'X';
+      *r++ = '\'';
+      /* we cheat by knowing that we store full bytes zero padded */
+      for (i=0; i<len; i+=2, sp++) {
+       *r++ = HEXDIG((*sp)>>4);
+       *r++ = HEXDIG((*sp) & 0xF);
+      }
+      /* Go back one step if we printed a hex number that was not part
+        of the bitstring anymore */
+      if (i==len+1)
+       r--;
+      *r++ = '\'';
+      *r = '\0';
+    }
+  return result;
+}
+
+/* zpbitsout -
+ *    Prints the string a bits
+ */
+char *
+zpbitsout(char *s)
+{
+  char    *result, *r;
+  VarBit   sp;
+  bits8    x;
+  int     i, k, len;
+  
+  if (s == NULL)
+    {
+      result = (char *) palloc(2);
+      result[0] = '-';
+      result[1] = '\0';
+    }
+  else
+    {
+      len = VARBITLEN(s);
+      result = (char *) palloc(len + 4);
+      sp = (bits8 *) VARBITS(s);
+      r = result;
+      *r++ = 'B';
+      *r++ = '\'';
+      for (i=0; i<len-BITSPERBYTE; i+=BITSPERBYTE, sp++) {
+       x = *sp;
+       for (k=0; k<BITSPERBYTE; k++) 
+         {
+           *r++ = (x & BITHIGH) ? '1' : '0';
+           x <<= 1;
+         }
+      }
+      x = *sp;
+      for (k=i; k<len; k++)
+       {
+         *r++ = (x & BITHIGH) ? '1' : '0';
+         x <<= 1;
+       }
+      *r++ = '\'';
+      *r = '\0';
+    }
+  return result;
+}
+
+
+/*
+ * varbitin -
+ *       converts a string to the internal representation of a bitstring.
+*/
+char *
+varbitin(char *s, int dummy,  int32 atttypmod)
+{
+  char *result, 
+    *sp;               /* pointer into the character string  */
+  bits8 *r;
+  int len,             /* Length of the whole data structure */
+    bitlen,            /* Number of bits in the bit string   */
+    slen;              /* Length of the input string         */
+  int bit_not_hex;
+  int i, bc, ipad;
+  bits8 x, y;
+
+
+  if (s == NULL)
+    return NULL;
+
+  /* Check that the first character is a b or an x */
+  if (s[0]=='b' || s[0]=='B') 
+      bit_not_hex = 1;
+  else if (s[0]=='x' || s[0]=='X') 
+      bit_not_hex = 0;
+  else 
+    elog(ERROR, "zpbitin: %s is not a valid bitstring",s);
+
+  slen = strlen(s) - 1;
+  /* Determine bitlength from input string */
+  bitlen = slen;
+  if (!bit_not_hex)
+    bitlen *= 4;
+  
+  /* Sometimes atttypmod is not supplied. If it is supplied we need to make
+     sure that the bitstring fits. Note that the number of infered bits can
+     be larger than the number of actual bits needed, but only if we are 
+     reading a hex string and not by more than 3 bits, as a hex string gives 
+     and accurate length upto 4 bits */
+  if (atttypmod > -1)
+    if (bitlen>atttypmod && bit_not_hex || bitlen>atttypmod+3 && !bit_not_hex)
+         elog(ERROR, "varbitin: bit string of size %d cannot be written into varying bits(%d)",
+              bitlen,atttypmod);
+
+
+  len = VARBITDATALEN(bitlen);
+
+  if (len > MaxAttrSize)
+    elog(ERROR, "varbitin: length of bit() must be less than %d",
+        (MaxAttrSize-VARHDRSZ-VARBITHDRSZ)*BITSPERBYTE);
+
+  result = (char *) palloc(len);
+  /* set to 0 so that *r is always initialised and strin is zero-padded */
+  memset(result, 0, len);
+  VARSIZE(result) = len;
+  VARBITLEN(result) = bitlen;
+
+  /* We need to read the bitstring from the end, as we store it least 
+     significant byte first. s points to the byte before the beginning
+     of the bitstring */
+  sp = s + 1;
+  r = (VarBit) VARBITS(result);
+  if (bit_not_hex) 
+    {
+      /* Parse the bit representation of the string */
+      x = BITHIGH;
+      for (bc = 0; sp != s+slen+1; sp++, bc++)
+       {
+         if (*sp=='1')
+           *r |= x;
+         if (bc==7) {
+           bc = 0;
+           x = BITHIGH;
+           r++;
+         } else 
+           x >>= 1;
+       }
+     }
+  else 
+    {
+      for (bc = 0; sp != s+slen+1; sp++)
+       {
+         if (*sp>='0' && *sp<='9') 
+           x = (bits8) (*sp - '0');
+         else if (*sp>='A' && *sp<='F') 
+           x = (bits8) (*sp - 'A') + 10;
+         else if (*sp>='a' && *sp<='f') 
+           x = (bits8) (*sp - 'a') + 10;
+         else 
+           elog(ERROR,"Cannot parse %c as a hex digit",*sp);
+         if (bc) {
+           bc = 0;
+           *r++ |= x;
+         } else {
+           bc++;
+           *r = x<<4;
+         }
+       }
+    }
+
+  if (bitlen > atttypmod) {
+    /* Check that this fitted */
+    r = (bits8 *) (result + len - 1);
+    ipad = VARBITPAD(result);
+    /* The bottom ipad bits of the byte pointed to by r need to be zero */
+    if (((*r << (BITSPERBYTE-ipad)) & BITMASK) > 0)
+      elog(ERROR, "varbitin: bit string too large for varying bit(%d) data type",
+          atttypmod);
+  }
+
+  return result;
+}
+
+/*
+  the zpbitout routines are fine for varying bits as well 
+*/
+
+
+/*
+ * Comparison operators
+ *
+ * We only need one set of comparison operators for bitstrings, as the lengths
+ * are stored in the same way for zero-padded and varying bit strings. 
+ *
+ * Note that the standard is not unambiguous about the comparison between 
+ * zero-padded bit strings and varying bitstrings. If the same value is written
+ * into a zero padded bitstring as into a varying bitstring, but the zero 
+ * padded bitstring has greater length, it will be bigger. 
+ *
+ * Zeros from the beginning of a bitstring cannot simply be ignored, as they
+ * may be part of a bit string and may be significant.
+ */
+
+bool
+biteq (char *arg1, char *arg2)
+{
+  int bitlen1,
+    bitlen2;
+  bits8 *p1, *p2;
+
+  if (!PointerIsValid(arg1) || !PointerIsValid(arg2))
+    return (bool) 0;
+  bitlen1 = VARBITLEN(arg1);
+  bitlen2 = VARBITLEN(arg2);
+  if (bitlen1 != bitlen2)
+    return (bool) 0;
+  
+  /* bit strings are always stored in a full number of bytes */
+  return memcmp((void *)VARBITS(arg1),(void *)VARBITS(arg2),
+               VARBITBYTES(arg1)) == 0;
+}
+
+bool
+bitne (char *arg1, char *arg2)
+{
+  int bitlen1,
+    bitlen2;
+  bits8 *p1, *p2;
+
+  if (!PointerIsValid(arg1) || !PointerIsValid(arg2))
+    return (bool) 0;
+  bitlen1 = VARBITLEN(arg1);
+  bitlen2 = VARBITLEN(arg2);
+  if (bitlen1 != bitlen2)
+    return (bool) 1;
+  
+  /* bit strings are always stored in a full number of bytes */
+  return memcmp((void *)VARBITS(arg1),(void *)VARBITS(arg2),
+               VARBITBYTES(arg1)) != 0;
+}
+
+/* bitcmp
+ * 
+ * Compares two bitstrings and returns -1, 0, 1 depending on whether the first
+ * string is smaller, equal, or bigger than the second. All bits are considered
+ * and additional zero bits may make one string smaller/larger than the other,
+ * even if their zero-padded values would be the same.
+ *   Anything is equal to undefined.
+ */
+int 
+bitcmp (char *arg1, char *arg2)
+{
+  int bitlen1, bytelen1,
+    bitlen2, bytelen2;
+  bits8 *p1, *p2;
+  int cmp;
+
+  if (!PointerIsValid(arg1) || !PointerIsValid(arg2))
+    return (bool) 0;
+  bytelen1 = VARBITBYTES(arg1);  
+  bytelen2 = VARBITBYTES(arg2);
+  
+  cmp = memcmp(VARBITS(arg1),VARBITS(arg2),Min(bytelen1,bytelen2));
+  if (cmp==0) {
+    bitlen1 = VARBITLEN(arg1);
+    bitlen2 = VARBITLEN(arg2);
+    if (bitlen1 != bitlen2) 
+      return bitlen1 < bitlen2 ? -1 : 1;
+  }
+  return cmp;
+}
+
+bool
+bitlt (char *arg1, char *arg2)
+{
+  return (bool) (bitcmp(arg1,arg2) == -1);
+}
+
+bool
+bitle (char *arg1, char *arg2)
+{
+  return (bool) (bitcmp(arg1,arg2) <= 0);
+}
+
+bool
+bitge (char *arg1, char *arg2)
+{
+  return (bool) (bitcmp(arg1,arg2) >= 0);
+}
+
+bool
+bitgt (char *arg1, char *arg2)
+{
+  return (bool) (bitcmp(arg1,arg2) == 1);
+}
+
+/* bitcat
+ * Concatenation of bit strings
+ */
+char *
+bitcat (char *arg1, char *arg2)
+{
+  int bitlen1, bitlen2, bytelen, bit1pad, bit2shift;
+  char *result;
+  bits8 *pr, *pa;
+
+  if (!PointerIsValid(arg1) || !PointerIsValid(arg2))
+    return NULL;
+
+  bitlen1 = VARBITLEN(arg1);
+  bitlen2 = VARBITLEN(arg2);
+
+  bytelen = VARBITDATALEN(bitlen1+bitlen2);
+  
+  result = (char *) palloc(bytelen*sizeof(bits8));
+  VARSIZE(result) = bytelen;
+  VARBITLEN(result) = bitlen1+bitlen2;
+  printf("%d %d %d \n",VARBITBYTES(arg1),VARBITLEN(arg1),VARBITPAD(arg1));
+  /* Copy the first bitstring in */
+  memcpy(VARBITS(result),VARBITS(arg1),VARBITBYTES(arg1));
+  /* Copy the second bit string */
+  bit1pad = VARBITPAD(arg1);
+  if (bit1pad==0) 
+    {
+      memcpy(VARBITS(result)+VARBITBYTES(arg1),VARBITS(arg2),
+            VARBITBYTES(arg2));
+    }
+  else if (bitlen2>0)
+    {
+      /* We need to shift all the results to fit */
+      bit2shift = BITSPERBYTE - bit1pad;
+      pa = (VarBit) VARBITS(arg2);
+      pr = (VarBit) VARBITS(result)+VARBITBYTES(arg1)-1;
+      for ( ; pa < VARBITEND(arg2); pa++) {
+       *pr = *pr | ((*pa >> bit2shift) & BITMASK);
+       pr++;
+       if (pr < VARBITEND(result))
+         *pr = (*pa << bit1pad) & BITMASK;
+      }
+    }
+
+  return result;
+}
+
+/* bitsubstr
+ * retrieve a substring from the bit string. 
+ * Note, s is 1-based.
+ * SQL draft 6.10 9)
+ */
+char * 
+bitsubstr (char *arg, int32 s, int32 l)
+{
+  int bitlen,
+    rbitlen,
+    len,
+    ipad,
+    ishift,
+    i;
+  int e, s1, e1;
+  char * result;
+  bits8 mask, *r, *ps;
+
+  if (!PointerIsValid(arg))
+    return NULL;
+
+  bitlen = VARBITLEN(arg);
+  e = s+l;
+  s1 = Max(s,1);
+  e1 = Min(e,bitlen+1);
+  if (s1>bitlen || e1<1) 
+    {
+      /* Need to return a null string */
+      len = VARBITDATALEN(0);
+      result = (char *) palloc(len);
+      VARBITLEN(result) = 0;
+      VARSIZE(result) = len;
+    } 
+  else 
+    {
+      /* OK, we've got a true substring starting at position s1-1 and 
+        ending at position e1-1 */
+      rbitlen = e1-s1;
+      len = VARBITDATALEN(rbitlen);
+      result = (char *) palloc(len);
+      VARBITLEN(result) = rbitlen;
+      VARSIZE(result) = len;
+      /* Are we copying from a byte boundary? */
+      if ((s1-1)%BITSPERBYTE==0) 
+       {
+         /* Yep, we are copying bytes */
+         len -= VARHDRSZ + VARBITHDRSZ;
+         memcpy(VARBITS(result),VARBITS(arg)+(s1-1)/BITSPERBYTE,len);
+       } 
+      else 
+       {
+         /* Figure out how much we need to shift the sequence by */
+         ishift = (s1-1)%BITSPERBYTE;
+         r = (VarBit) VARBITS(result);
+         ps = (VarBit) VARBITS(arg) + (s1-1)/BITSPERBYTE;
+         for (i=0; i<len; i++) 
+           {
+             *r = (*ps <<ishift) & BITMASK;
+             if ((++ps) < VARBITEND(arg))
+               *r |= *ps >>(BITSPERBYTE-ishift);
+             r++;
+           }
+       }
+      /* Do we need to pad at the end? */
+      ipad = VARBITPAD(result);
+      if (ipad > 0) 
+       {
+         mask = BITMASK << ipad;
+         *(VARBITS(result) + len - 1) &= mask;
+       }
+    }
+
+  return result;
+}
+
+/* bitand
+ * perform a logical AND on two bit strings. The result is automatically
+ * truncated to the shorter bit string
+ */
+char *
+bitand (char * arg1, char * arg2)
+{
+  int len,
+    i;
+  char *result;
+  bits8 *p1, 
+    *p2, 
+    *r;
+
+  if (!PointerIsValid(arg1) || !PointerIsValid(arg2))
+    return (bool) 0;
+
+  len = Min(VARSIZE(arg1),VARSIZE(arg2));
+  result = (char *) palloc(len);
+  VARSIZE(result) = len;
+  VARBITLEN(result) = Min(VARBITLEN(arg1),VARBITLEN(arg2));
+
+  p1 = (bits8 *) VARBITS(arg1);
+  p2 = (bits8 *) VARBITS(arg2);
+  r = (bits8 *) VARBITS(result);
+  for (i=0; i<Min(VARBITBYTES(arg1),VARBITBYTES(arg2)); i++)
+    *r++ = *p1++ & *p2++;
+  
+  /* Padding is not needed as & of 0 pad is 0 */
+  
+  return result;
+}
+
+/* bitor
+ * perform a logical OR on two bit strings. The result is automatically
+ * truncated to the shorter bit string.
+ */
+char *
+bitor (char * arg1, char * arg2)
+{
+  int len,
+    i;
+  char *result;
+  bits8 *p1, 
+    *p2, 
+    *r;
+  bits8 mask;
+
+  if (!PointerIsValid(arg1) || !PointerIsValid(arg2))
+    return (bool) 0;
+
+  len = Min(VARSIZE(arg1),VARSIZE(arg2));
+  result = (char *) palloc(len);
+  VARSIZE(result) = len;
+  VARBITLEN(result) = Min(VARBITLEN(arg1),VARBITLEN(arg2));
+
+  p1 = (bits8 *) VARBITS(arg1);
+  p2 = (bits8 *) VARBITS(arg2);
+  r = (bits8 *) VARBITS(result);
+  for (i=0; i<Min(VARBITBYTES(arg1),VARBITBYTES(arg2)); i++)
+    *r++ = *p1++ | *p2++;
+
+  /* Pad the result */
+  mask = BITMASK << VARBITPAD(result);
+  *r &= mask;
+  
+  return result;
+}
+
+/* bitxor
+ * perform a logical XOR on two bit strings. The result is automatically
+ * truncated to the shorter bit string.
+ */
+char *
+bitxor (char * arg1, char * arg2)
+{
+  int len,
+    i;
+  char *result;
+  bits8 *p1, 
+    *p2, 
+    *r;
+  bits8 mask;
+
+  if (!PointerIsValid(arg1) || !PointerIsValid(arg2))
+    return (bool) 0;
+
+  len = Min(VARSIZE(arg1),VARSIZE(arg2));
+  result = (char *) palloc(len);
+  VARSIZE(result) = len;
+  VARBITLEN(result) = Min(VARBITLEN(arg1),VARBITLEN(arg2));
+
+  p1 = (bits8 *) VARBITS(arg1);
+  p2 = (bits8 *) VARBITS(arg2);
+  r = (bits8 *) VARBITS(result);
+  for (i=0; i<Min(VARBITBYTES(arg1),VARBITBYTES(arg2)); i++)
+    {
+      *r++ = *p1++ ^ *p2++;
+    }
+
+  /* Pad the result */
+  mask = BITMASK << VARBITPAD(result);
+  *r &= mask;
+  
+  return result;
+}
+
+/* bitnot
+ * perform a logical NOT on a bit strings.
+ */
+char *
+bitnot (char * arg)
+{
+  int len;
+  char *result;
+  bits8 *p, 
+    *r;
+  bits8 mask;
+
+  if (!PointerIsValid(arg))
+    return (bool) 0;
+
+  result = (char *) palloc(VARSIZE(arg));
+  VARSIZE(result) = VARSIZE(arg);
+  VARBITLEN(result) = VARBITLEN(arg);
+
+  p = (bits8 *) VARBITS(arg);
+  r = (bits8 *) VARBITS(result);
+  for ( ; p < VARBITEND(arg); p++, r++)
+    *r = ~*p;
+
+  /* Pad the result */
+  mask = BITMASK << VARBITPAD(result);
+  *r &= mask;
+  
+  return result;
+}
+
+/* bitshiftleft
+ * do a left shift (i.e. to the beginning of the string) of the bit string
+ */
+char *
+bitshiftleft (char * arg, int shft)
+{
+  int byte_shift, ishift, len;
+  char *result;
+  bits8 *p, 
+    *r;
+
+  if (!PointerIsValid(arg))
+    return (bool) 0;
+
+  /* Negative shift is a shift to the right */
+  if (shft < 0) 
+    return bitshiftright(arg, -shft);
+
+  result = (char *) palloc(VARSIZE(arg));
+  VARSIZE(result) = VARSIZE(arg);
+  VARBITLEN(result) = VARBITLEN(arg);
+  r = (bits8 *) VARBITS(result);
+
+  byte_shift = shft/BITSPERBYTE;
+  ishift = shft % BITSPERBYTE;
+  p = ((bits8 *) VARBITS(arg)) + byte_shift;
+  
+  if (ishift == 0) {
+    /* Special case: we can do a memcpy */
+    len = VARBITBYTES(arg) - byte_shift;
+    memcpy(r, p, len);
+    memset(r+len, 0, byte_shift);
+  } else {
+    for ( ; p < VARBITEND(arg); r++) {
+      *r = *p <<ishift;
+      if ((++p) < VARBITEND(arg))
+       *r |= *p >>(BITSPERBYTE-ishift);
+    }
+    for ( ; r < VARBITEND(result) ; r++ ) 
+      *r = (bits8) 0;
+  }
+
+  return result;
+}
+
+/* bitshiftright
+ * do a right shift (i.e. to the beginning of the string) of the bit string
+ */
+char *
+bitshiftright (char * arg, int shft)
+{
+  int byte_shift, ishift, len;
+  char *result;
+  bits8 *p, 
+    *r;
+
+  if (!PointerIsValid(arg))
+    return (bool) 0;
+
+  /* Negative shift is a shift to the left */
+  if (shft < 0) 
+    return bitshiftleft(arg, -shft);
+
+  result = (char *) palloc(VARSIZE(arg));
+  VARSIZE(result) = VARSIZE(arg);
+  VARBITLEN(result) = VARBITLEN(arg);
+  r = (bits8 *) VARBITS(result);
+
+  byte_shift = shft/BITSPERBYTE;
+  ishift = shft % BITSPERBYTE;
+  p = (bits8 *) VARBITS(arg);
+
+  /* Set the first part of the result to 0 */
+  memset(r, 0, byte_shift);
+  
+  if (ishift == 0) 
+    {
+      /* Special case: we can do a memcpy */
+      len = VARBITBYTES(arg) - byte_shift;
+      memcpy(r+byte_shift, p, len);
+    } 
+  else 
+    {
+      r += byte_shift;
+      *r = 0;    /* Initialise first byte */
+      for ( ; r < VARBITEND(result); p++) {
+       *r |= *p >> ishift;
+       if ((++r) < VARBITEND(result))
+         *r = (*p <<(BITSPERBYTE-ishift)) & BITMASK;
+      }
+    }
+
+  return result;
+}
diff --git a/contrib/bit/varbit.h b/contrib/bit/varbit.h
new file mode 100644 (file)
index 0000000..af55f48
--- /dev/null
@@ -0,0 +1,48 @@
+#include "postgres.h"
+
+typedef bits8  *VarBit;
+typedef uint32 BitIndex;
+
+#define HEXDIG(z)    (z)<10 ? ((z)+'0') : ((z)-10+'A')
+
+
+#define BITSPERBYTE            8
+#define VARBITHDRSZ            sizeof(int32)
+/* Number of bits in this bit string */
+#define VARBITLEN(PTR)         (((struct varlena *)VARDATA(PTR))->vl_len)
+/* Pointer tp the first byte containing bit string data */
+#define VARBITS(PTR)           (((struct varlena *)VARDATA(PTR))->vl_dat)
+/* Number of bytes in the data section of a bit string */
+#define VARBITBYTES(PTR)       (VARSIZE(PTR) - VARHDRSZ - VARBITHDRSZ)
+/* Padding of the bit string at the end */
+#define VARBITPAD(PTR)         (VARBITBYTES(PTR)*BITSPERBYTE - VARBITLEN(PTR))
+/* Number of bytes needed to store a bit string of a given length */
+#define VARBITDATALEN(BITLEN)  (BITLEN/BITSPERBYTE + \
+                                 (BITLEN%BITSPERBYTE > 0 ? 1 : 0) + \
+                                       VARHDRSZ + VARBITHDRSZ)
+/* pointer beyond the end of the bit string (like end() in STL containers) */
+#define VARBITEND(PTR)         ((bits8 *) (PTR + VARSIZE(PTR)))
+/* Mask that will cover exactly one byte, i.e. BITSPERBYTE bits */
+#define BITMASK                        0xFF
+#define BITHIGH                 0x80
+
+
+char * zpbitin(char *s, int dummy,  int32 atttypmod);
+char * zpbitout(char *s);
+char * zpbitsout(char *s);
+char * varbitin(char *s, int dummy,  int32 atttypmod);
+bool biteq (char *arg1, char *arg2);
+bool bitne (char *arg1, char *arg2);
+bool bitge (char *arg1, char *arg2);
+bool bitgt (char *arg1, char *arg2);
+bool bitle (char *arg1, char *arg2);
+bool bitlt (char *arg1, char *arg2);
+int bitcmp (char *arg1, char *arg2);
+char * bitand (char * arg1, char * arg2);
+char * bitor (char * arg1, char * arg2);
+char * bitxor (char * arg1, char * arg2);
+char * bitnot (char * arg);
+char * bitshiftright (char * arg, int shft);
+char * bitshiftleft (char * arg, int shft);
+char * bitcat (char *arg1, char *arg2);
+char * bitsubstr (char *arg, int32 s, int32 l);
diff --git a/contrib/bit/vartest.c b/contrib/bit/vartest.c
new file mode 100644 (file)
index 0000000..732141e
--- /dev/null
@@ -0,0 +1,172 @@
+#include "postgres.h"
+#include "varbit.h"
+#include <stdio.h>
+
+const int numb = 8;
+/*
+const char *b[] = { "B0010", "B11011011", "B0001", "X3F12", "X27", "B",
+                    "X11", "B100111"};
+int atttypmod[] = {-1, -1, -1,-1,-1,-1,-1,-1 };
+*/
+const char *b[] = { "B0010", "B11011011", "B10001", "X3D12", "X27", "B",
+                    "X11", "B100111"};
+int atttypmod[] = { 7, 9, 6, 18, 11, 6, -1, -1 };
+
+
+void print_details (unsigned char *s) 
+{
+  int i;
+  printf ("Length in bytes    : %d\n",VARSIZE(s));
+  printf ("Length of bitstring: %d\n",VARBITLEN(s));
+  for (i=8; i<VARSIZE(s); i++) 
+    printf ("%X%X ",s[i]>>4,s[i]&0xF);
+  printf("\n");
+}
+
+void
+main () 
+{
+  int i, j;
+  char *s[numb];
+
+    for (i=0; i<numb; i++) {
+    printf ("Input: %s\n",b[i]);
+    s[i] = zpbitin(b[i], 0, atttypmod[i]);
+    //print_details(s[i]);
+    printf ("%s = %s\n",zpbitout(s[i]),zpbitsout(s[i]));
+  }
+
+  printf ("\nCOMPARISONS:\n");
+  for (i=0; i<numb; i++)
+    for (j=i+1; j<numb; j++)
+      printf("%s <=> %s = %d\n",zpbitsout(s[i]),zpbitsout(s[j]),
+            bitcmp(s[i],s[j]));
+
+  printf ("\nCONCATENATION:\n");
+  for (i=0; i<numb; i++)
+    for (j=i+1; j<numb; j++)
+      printf("%s || %s = %s\n",zpbitsout(s[i]),zpbitsout(s[j]),
+            zpbitsout(bitcat(s[i],s[j])));
+
+  printf("\nSUBSTR:\n");
+  printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),1,8,
+        zpbitsout(bitsubstr(s[3],1,8)));
+  printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),9,8,
+        zpbitsout(bitsubstr(s[3],9,8)));
+ printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),1,9,
+        zpbitsout(bitsubstr(s[3],1,9)));
+ printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),3,5,
+        zpbitsout(bitsubstr(s[3],3,5)));
+ printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),3,9,
+        zpbitsout(bitsubstr(s[3],3,9)));
+ printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),3,17,
+        zpbitsout(bitsubstr(s[3],3,17)));
+  printf ("\nLOGICAL AND:\n");
+  for (i=0; i<numb; i++)
+    for (j=i+1; j<numb; j++)
+      printf("%s & %s = %s\n",zpbitsout(s[i]),zpbitsout(s[j]),
+            zpbitsout(bitand(s[i],s[j])));
+
+  printf ("\nLOGICAL OR:\n");
+  for (i=0; i<numb; i++)
+    for (j=i+1; j<numb; j++)
+      printf("%s | %s = %s\n",zpbitsout(s[i]),zpbitsout(s[j]),
+            zpbitsout(bitor(s[i],s[j])));
+
+  printf ("\nLOGICAL XOR:\n");
+  for (i=0; i<numb; i++)
+    for (j=i+1; j<numb; j++)
+      printf("%s ^ %s = %s\n",zpbitsout(s[i]),zpbitsout(s[j]),
+            zpbitsout(bitxor(s[i],s[j])));
+  
+  printf ("\nLOGICAL NOT:\n");
+  for (i=0; i<numb; i++)
+    printf("~%s = %s\n",zpbitsout(s[i]),zpbitsout(bitnot(s[i])));
+
+
+  printf ("\nSHIFT LEFT:\n");
+  for (i=0; i<numb; i++) {
+    printf("%s\n",zpbitsout(s[i]));
+    for (j=0; j<=VARBITLEN(s[i]); j++)
+      printf("\t%3d\t%s\n",j,zpbitsout(bitshiftleft(s[i],j)));
+  }
+
+  printf ("\nSHIFT RIGHT:\n");
+  for (i=0; i<numb; i++) {
+    printf("%s\n",zpbitsout(s[i]));
+    for (j=0; j<=VARBITLEN(s[i]); j++)
+      printf("\t%3d\t%s\n",j,zpbitsout(bitshiftright(s[i],j)));
+  }
+
+  printf ("\n\n ********** VARYING **********\n");
+  for (i=0; i<numb; i++) {
+    printf ("Input: %s\n",b[i]);
+    s[i] = varbitin(b[i], 0, atttypmod[i]);
+    /*print_details(s);*/
+    printf ("%s\n",zpbitout(s[i]));
+    printf ("%s\n",zpbitsout(s[i]));
+  }
+
+  printf ("\nCOMPARISONS:\n");
+  for (i=0; i<numb; i++)
+    for (j=i+1; j<numb; j++)
+      printf("%s <=> %s = %d\n",zpbitsout(s[i]),zpbitsout(s[j]),
+            bitcmp(s[i],s[j]));
+
+  printf ("\nCONCATENATION:\n");
+  for (i=0; i<numb; i++)
+    for (j=i+1; j<numb; j++)
+      printf("%s || %s = %s\n",zpbitsout(s[i]),zpbitsout(s[j]),
+            zpbitsout(bitcat(s[i],s[j])));
+
+  printf("\nSUBSTR:\n");
+  printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),1,8,
+        zpbitsout(bitsubstr(s[3],1,8)));
+  printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),9,8,
+        zpbitsout(bitsubstr(s[3],9,8)));
+ printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),1,9,
+        zpbitsout(bitsubstr(s[3],1,9)));
+ printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),3,5,
+        zpbitsout(bitsubstr(s[3],3,5)));
+ printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),3,9,
+        zpbitsout(bitsubstr(s[3],3,9)));
+ printf("%s (%d,%d) => %s\n",zpbitsout(s[3]),3,17,
+        zpbitsout(bitsubstr(s[3],3,17)));
+  printf ("\nLOGICAL AND:\n");
+  for (i=0; i<numb; i++)
+    for (j=i+1; j<numb; j++)
+      printf("%s & %s = %s\n",zpbitsout(s[i]),zpbitsout(s[j]),
+            zpbitsout(bitand(s[i],s[j])));
+
+  printf ("\nLOGICAL OR:\n");
+  for (i=0; i<numb; i++)
+    for (j=i+1; j<numb; j++)
+      printf("%s | %s = %s\n",zpbitsout(s[i]),zpbitsout(s[j]),
+            zpbitsout(bitor(s[i],s[j])));
+
+  printf ("\nLOGICAL XOR:\n");
+  for (i=0; i<numb; i++)
+    for (j=i+1; j<numb; j++)
+      printf("%s ^ %s = %s\n",zpbitsout(s[i]),zpbitsout(s[j]),
+            zpbitsout(bitxor(s[i],s[j])));
+  
+  printf ("\nLOGICAL NOT:\n");
+  for (i=0; i<numb; i++)
+    printf("~%s = %s\n",zpbitsout(s[i]),zpbitsout(bitnot(s[i])));
+
+
+  printf ("\nSHIFT LEFT:\n");
+  for (i=0; i<numb; i++) {
+    printf("%s\n",zpbitsout(s[i]));
+    for (j=0; j<=VARBITLEN(s[i]); j++)
+      printf("\t%3d\t%s\n",j,zpbitsout(bitshiftleft(s[i],j)));
+  }
+
+  printf ("\nSHIFT RIGHT:\n");
+  for (i=0; i<numb; i++) {
+    printf("%s\n",zpbitsout(s[i]));
+    for (j=0; j<=VARBITLEN(s[i]); j++)
+      printf("\t%3d\t%s\n",j,zpbitsout(bitshiftright(s[i],j)));
+  }
+
+}