/* Serialization of integers into a little-endian sequence of bytes

 Converts a little-endian byte sequence to an unsigned integer.
 > bytesize: number of given 8-bit bytes of the integer,
             < intDsize/8*uintWC_max
 > bufferptr: address of bytesize bytes in GC-invariant memory
 < result: an integer >= 0 with I_integer_length(result) <= 8*bytesize
 can trigger GC */
modexp maygc object LEbytes_to_UI (uintL bytesize, const uintB* bufferptr) {
  var gcv_object_t fake; fake = FAKE_8BIT_VECTOR(bufferptr);
  return LESbvector_to_UI(bytesize,&fake);
}

/* Converts a little-endian byte sequence to an unsigned integer.
 > bytesize: number of given 8-bit bytes of the integer,
             < intDsize/8*uintWC_max
 > *buffer_: address of a simple-8bit-vector (or of a fake)
             containing bytesize bytes of memory
 < result: an integer >= 0 with I_integer_length(result) <= 8*bytesize
 can trigger GC */
global maygc object LESbvector_to_UI (uintL bytesize, const gcv_object_t* buffer_) {
  /* Normalize number in buffer: */
  var uintB* bufferptr = &TheSbvector(*buffer_)->data[bytesize-1];
  var uintL count = bytesize;
  while ((!(count==0)) && (*bufferptr==0)) { count--; bufferptr--; }
  /* Make number: */
  if                            /* at most oint_data_len Bits ? */
    ((count <= floor(oint_data_len,8))
     || ((count == floor(oint_data_len,8)+1)
         && (*bufferptr < bit(oint_data_len%8)))) {
    /* yes -> build Fixnum >=0 : */
    var uintV value = 0;
    while (count != 0) { value = (value<<8) | *bufferptr--; count--; }
    return fixnum(value);
  }
  /* no -> build Bignum >0 : */
  var uintL digitcount = floor(count,(intDsize/8));
  if (((count%(intDsize/8)) > 0) || (*bufferptr & bit(7)))
    digitcount++;
  /* As bitsize < intDsize*uintWC_max,
   digitcount <= ceiling((bitsize+1)/intDsize) <= uintWC_max . */
  var object big = allocate_bignum(digitcount,0); /* new Bignum >0 */
  TheBignum(big)->data[0] = 0;  /* set highest Digit to 0 */
  /* Fill remaining Digits from right to left,
   thereby translate sequence of Bytes into sequence of uintD: */
  bufferptr = &TheSbvector(*buffer_)->data[0];
  #if BIG_ENDIAN_P
  {
    var uintB* bigptr = (uintB*)(&TheBignum(big)->data[digitcount]);
    dotimespL(count,count, { *--bigptr = *bufferptr++; } );
  }
  #else
  {
    var uintD* bigptr = &TheBignum(big)->data[digitcount];
    var uintL count2;
    #define GET_NEXT_BYTE(i)  digit |= ((uintD)(*bufferptr++) << (8*i));
    dotimespL(count2,floor(count,intDsize/8), {
      var uintD digit = 0;
      DOCONSTTIMES(intDsize/8,GET_NEXT_BYTE); /* GET_NEXT_BYTE(0..intDsize/8-1) */
      *--bigptr = digit;
    });
    #undef GET_NEXT_BYTE
    count2 = count % (intDsize/8);
    if (count2>0) {
      var uintL shiftcount = 0;
      var uintD digit = (uintD)(*bufferptr++);
      dotimesL(count2,count2-1, {
        shiftcount += 8;
        digit |= ((uintD)(*bufferptr++) << shiftcount);
      });
      *--bigptr = digit;
    }
  }
  #endif
  /* Since (intDsize/8)*(digitcount-1) <= count <= (intDsize/8)*digitcount
   everything is filled. */
  return big;
}

/* Converts a little-endian byte sequence to an integer.
 > bytesize: number of given 8-bit bytes of the integer, > 0,
             < intDsize/8*uintWC_max
 > bufferptr: address of bytesize bytes in GC-invariant memory
 < result: an integer with I_integer_length(result) < 8*bytesize
 can trigger GC */
modexp maygc object LEbytes_to_I (uintL bytesize, const uintB* bufferptr) {
  var gcv_object_t fake; fake = FAKE_8BIT_VECTOR(bufferptr);
  return LESbvector_to_I(bytesize,&fake);
}

/* Converts a little-endian byte sequence to an integer.
 > bytesize: number of given 8-bit bytes of the integer, > 0,
             < intDsize/8*uintWC_max
 > *buffer_: address of a simple-8bit-vector (or of a fake)
             containing bytesize bytes of memory
 < result: an integer with I_integer_length(result) < 8*bytesize
 can trigger GC */
global maygc object LESbvector_to_I (uintL bytesize, const gcv_object_t* buffer_) {
  /* Normalize number in buffer: */
  var uintB* bufferptr = &TheSbvector(*buffer_)->data[bytesize-1];
  var sintD sign;
  var uintL count = bytesize;
  if (!(*bufferptr & bit(7))) {
    sign = 0;
    /* Normalize, highest Bit must remain 0: */
    while ((count>=2) && (*bufferptr==0)
           && !(*(bufferptr-1) & bit(7))) {
      count--; bufferptr--;
    }
    /* Make number: */
    if      /* at most oint_data_len+1 Bits, count <2^oint_data_len ? */
      ((count <= floor(oint_data_len,8))
       || ((count == floor(oint_data_len,8)+1)
           && (*bufferptr < bit(oint_data_len%8)))) {
      /* yes -> build Fixnum >=0: */
      var uintV value = 0;
      while (count != 0) { value = (value<<8) | *bufferptr--; count--; }
      return posfixnum(value);
    }
  } else {
    sign = -1;
    /* Normalize, highest Bit must remain 1: */
    while ((count>=2) && (*bufferptr==(uintB)(-1))
           && (*(bufferptr-1) & bit(7))) {
      count--; bufferptr--;
    }
    /* Make number: */
    if    /* at most oint_data_len+1 Bits, count >=-2^oint_data_len ? */
      ((count <= floor(oint_data_len,8))
       || ((count == floor(oint_data_len,8)+1)
           && (*bufferptr >= (uintB)(-bit(oint_data_len%8))))) {
      /* yes -> build Fixnum <0: */
      var uintV value = (uintV)(sintV)(-1);
      while (count != 0) { value = (value<<8) | *bufferptr--; count--; }
      return negfixnum(-wbitm(intVsize)+(oint)value);
    }
  }
  /* Make bignum: */
  var uintL digitcount = ceiling(count,(intDsize/8));
  /* As bitsize < intDsize*uintWC_max,
   digitcount <= ceiling(bitsize/intDsize) <= uintWC_max . */
  var object big = allocate_bignum(digitcount,(sintB)sign);
  TheBignum(big)->data[0] = sign; /* set highest Word to sign */
  /* Fill the remaining Digits from right to left,
   thereby translate sequence of Bytes into sequence of uintD: */
  bufferptr = &TheSbvector(*buffer_)->data[0];
  #if BIG_ENDIAN_P
  {
    var uintB* bigptr = (uintB*)(TheBignum(big)->data+digitcount);
    dotimespL(count,count, { *--bigptr = *bufferptr++; } );
  }
  #else
  {
    var uintD* bigptr = TheBignum(big)->data+digitcount;
    var uintL count2;
    #define GET_NEXT_BYTE(i)  digit |= ((uintD)(*bufferptr++) << (8*i));
    dotimespL(count2,floor(count,intDsize/8), {
      var uintD digit = 0;
      DOCONSTTIMES(intDsize/8,GET_NEXT_BYTE); /* GET_NEXT_BYTE(0..intDsize/8-1) */
      *--bigptr = digit;
    });
    #undef GET_NEXT_BYTE
    count2 = count % (intDsize/8);
    if (count2>0) {
      var uintL shiftcount = 0;
      var uintD digit = (uintD)(*bufferptr++);
      dotimesL(count2,count2-1, {
        shiftcount += 8;
        digit |= ((uintD)(*bufferptr++) << shiftcount);
      });
      *--bigptr = digit ^ (sign << (shiftcount+8));
    }
  }
  #endif
  /* Since (intDsize/8)*(digitcount-1) < count <= (intDsize/8)*digitcount
   everything is filled. */
  return big;
}

/* Converts an unsigned integer to a little-endian byte sequence.
 > obj: an integer
 > bitsize: maximum number of bits of the integer
 > bufferptr: pointer to bytesize = ceiling(bitsize,8) bytes of memory
 < false and bufferptr[0..bytesize-1] filled, if obj >= 0 and
                                              I_integer_length(obj) <= bitsize;
   true, if obj is out of range */
modexp bool UI_to_LEbytes (object obj, uintL bitsize, uintB* bufferptr) {
  if (!positivep(obj))
    return true;
  /* obj is an integer >=0. */
  var uintL bytesize = ceiling(bitsize,8);
  /* Transfer obj into the buffer: */
  {
    var uintL count = bytesize;
    if (posfixnump(obj)) {      /* obj is a Fixnum >=0 */
      var uintV value = posfixnum_to_V(obj);
      /* check value < 2^bitsize: */
      if (!((bitsize>=oint_data_len) || (value < vbit(bitsize))))
        return true;
      /* store value in Bitbuffer: */
      while (value != 0) {
        *bufferptr++ = (uint8)value; value = value>>8; count--;
      }
    } else {                    /* obj is a Bignum >0 */
      var uintL len = (uintL)Bignum_length(obj);
      /* check obj < 2^bitsize: */
      if (!((floor(bitsize,intDsize) >= len)
            || ((floor(bitsize,intDsize) == len-1)
                && (TheBignum(obj)->data[0] < bit(bitsize%intDsize)))))
        return true;
      #if BIG_ENDIAN_P
      {
        var uintB* ptr = (uintB*)&TheBignum(obj)->data[len];
        /* convert Digit-Length in Byte-Length: */
        len = (intDsize/8)*len;
        #define CHECK_NEXT_BYTE(i)  \
          if (((uintB*)(&TheBignum(obj)->data[0]))[i] != 0) goto len_ok; \
          len--;
        DOCONSTTIMES(intDsize/8,CHECK_NEXT_BYTE); /* CHECK_NEXT_BYTE(0..intDsize/8-1) */
        #undef CHECK_NEXT_BYTE
      len_ok:
        /* store obj in Bitbuffer: */
        count = count - len;
        dotimespL(len,len, { *bufferptr++ = *--ptr; } );
      }
      #else
      {
        var uintD* ptr = &TheBignum(obj)->data[len];
        len--;
        count -= (intDsize/8)*len;
        dotimesL(len,len, {
          var uintD digit = *--ptr;
          doconsttimes(intDsize/8, {
            *bufferptr++ = (uintB)digit; digit = digit >> 8;
          });
        });
        var uintD digit = *--ptr;
        doconsttimes(intDsize/8, {
          if (digit==0) goto ok;
          *bufferptr++ = (uintB)digit; digit = digit >> 8;
          count--;
        });
      ok: ;
      }
    #endif
    }
    if (count > 0) {
      begin_system_call();
      memset(bufferptr,0,count);
      end_system_call();
    }
  }
  return false;
}

/* Converts an integer to a little-endian byte sequence.
 > obj: an integer
 > bitsize: maximum number of bits of the integer, including the sign bit
 > bufferptr: pointer to bytesize = ceiling(bitsize,8) bytes of memory
 < false and bufferptr[0..bytesize-1] filled, if I_integer_length(obj) < bitsize;
   true, if obj is out of range */
modexp bool I_to_LEbytes (object obj, uintL bitsize, uintB* bufferptr) {
  /* obj is an integer. */
  var uintL bytesize = ceiling(bitsize,8);
  /* Transfer obj into the buffer: */
  {
    var uintL count = bytesize;
    var uintV sign = (sintV)(sintL)R_sign(obj);
    if (fixnump(obj)) { /* obj is a Fixnum */
      var uintV value = fixnum_to_V(obj); /* >=0 or <0, according to sign */
      /* check 0 <= value < 2^(bitsize-1) resp. -2^(bitsize-1) <= value < 0: */
      value = value^sign;
      if (!((bitsize>oint_data_len) || (value < bit(bitsize-1))))
        return true;
      /* store value^sign in Bitbuffer: */
      while (value != 0) {
        *bufferptr++ = (uint8)(value^sign); value = value>>8; count--;
      }
      if (count > 0) {
        begin_system_call();
        memset(bufferptr,(uint8)sign,count);
        end_system_call();
      }
    } else { /* obj is a Bignum */
      var uintL len = (uintL)Bignum_length(obj);
      /* check -2^(bitsize-1) <= obj < 2^(bitsize-1): */
      if (!((floor(bitsize,intDsize) >= len)
            || ((bitsize > intDsize*(len-1))
                && ((TheBignum(obj)->data[0] ^ (uintD)sign) <
                    bit((bitsize%intDsize)-1)))))
        return true;
      #if BIG_ENDIAN_P
      {
        var uintB* ptr = (uintB*)&TheBignum(obj)->data[len];
        /* convert Digit-Length in Byte-Length: */
        len = (intDsize/8)*len;
        #define CHECK_NEXT_BYTE(i)  \
          if (((uintB*)(&TheBignum(obj)->data[0]))[i] != (uintB)sign) goto len_ok; \
          len--;
        DOCONSTTIMES(intDsize/8,CHECK_NEXT_BYTE); /* CHECK_NEXT_BYTE(0..intDsize/8-1) */
        #undef CHECK_NEXT_BYTE
      len_ok:
        /* store obj in Bitbuffer: */
        count = count - len;
        dotimespL(len,len, { *bufferptr++ = *--ptr; } );
      }
      #else
      {
        var uintD* ptr = &TheBignum(obj)->data[len];
        len--;
        count -= (intDsize/8)*len;
        dotimesL(len,len, {
          var uintD digit = *--ptr;
          doconsttimes(intDsize/8, {
            *bufferptr++ = (uintB)digit; digit = digit >> 8;
          });
        });
        var sintD digit = *--ptr;
        doconsttimes(intDsize/8, {
          if (digit == (sintD)sign) goto ok;
          *bufferptr++ = (uintB)digit; digit = digit >> 8;
          count--;
        });
      ok: ;
      }
      #endif
      if (count > 0) {
        begin_system_call();
        memset(bufferptr,(uintB)sign,count);
        end_system_call();
      }
    }
  }
  return false;
}
