
(********************************************************************)
(*                                                                  *)
(*  bytedata.s7i  Convert byte data to and from strings             *)
(*  Copyright (C) 2009, 2013  Thomas Mertes                         *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library is distributed in the hope that it    *)
(*  will be useful, but WITHOUT ANY WARRANTY; without even the      *)
(*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *)
(*  PURPOSE.  See the GNU Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


include "file.s7i";
include "bigint.s7i";
include "bstring.s7i";


(**
 *  Get a zero terminated string from ''stri'' starting from ''currPos''.
 *  CurrPos is advanced after the zero ('\0\') character.
 *  @return the zero terminated string without the zero ('\0\') character.
 *)
const func string: getAsciiz (in string: stri, inout integer: currPos) is func
  result
    var string: resultStri is "";
  local
    var integer: startPos is 0;
  begin
    startPos := currPos;
    currPos := pos(stri, '\0\', startPos);
    if currPos = 0 then
      resultStri := stri[startPos ..];
      currPos := succ(length(stri));
    else
      resultStri := stri[startPos .. pred(currPos)];
      incr(currPos);
    end if;
  end func;


(**
 *  Convert a [[string]] of bytes to its hexadecimal representation.
 *  Each byte is represented by two hexadecimal digits.
 *  @return the hexadecimal representation of the given [[string]].
 *  @exception RANGE_ERROR When characters beyond '\255\' are present.
 *)
const func string: hex (in string: stri) is func
  result
    var string: hex is "";
  local
    var char: ch is ' ';
  begin
    for ch range stri do
      if ch > '\255\' then
        raise RANGE_ERROR;
      end if;
      hex &:= ord(ch) radix 16 lpad0 2;
    end for;
  end func;


(**
 *  Convert 16 lower bits of an integer into two little-endian chars.
 *  @return a string of length two with the little-endian representation.
 *)
const func string: int16AsTwoBytesLe (in integer: number) is
  return str(chr( number        mod 256)) &
         str(chr((number >>  8) mod 256));


(**
 *  Convert 32 lower bits of an integer into four little-endian chars.
 *  @return a string of length four with the little-endian representation.
 *)
const func string: int32AsFourBytesLe (in integer: number) is
  return str(chr( number        mod 256)) &
         str(chr((number >>  8) mod 256)) &
         str(chr((number >> 16) mod 256)) &
         str(chr((number >> 24) mod 256));


(**
 *  Convert 64 lower bits of an integer into eight little-endian chars.
 *  @return a string of length eight with the little-endian representation.
 *)
const func string: int64AsEightBytesLe (in integer: number) is
  return str(chr( number        mod 256)) &
         str(chr((number >>  8) mod 256)) &
         str(chr((number >> 16) mod 256)) &
         str(chr((number >> 24) mod 256)) &
         str(chr((number >> 32) mod 256)) &
         str(chr((number >> 40) mod 256)) &
         str(chr((number >> 48) mod 256)) &
         str(chr((number >> 56) mod 256));


(**
 *  Convert 16 lower bits of an integer into two big-endian chars.
 *  @return a string of length two with the big-endian representation.
 *)
const func string: int16AsTwoBytesBe (in integer: number) is
  return str(chr((number >>  8) mod 256)) &
         str(chr( number        mod 256));


(**
 *  Convert 32 lower bits of an integer into four big-endian chars.
 *  @return a string of length four with the big-endian representation.
 *)
const func string: int32AsFourBytesBe (in integer: number) is
  return str(chr((number >> 24) mod 256)) &
         str(chr((number >> 16) mod 256)) &
         str(chr((number >>  8) mod 256)) &
         str(chr( number        mod 256));


(**
 *  Convert 64 lower bits of an integer into eight big-endian chars.
 *  @return a string of length eight with the big-endian representation.
 *)
const func string: int64AsEightBytesBe (in integer: number) is
  return str(chr((number >> 56) mod 256)) &
         str(chr((number >> 48) mod 256)) &
         str(chr((number >> 40) mod 256)) &
         str(chr((number >> 32) mod 256)) &
         str(chr((number >> 24) mod 256)) &
         str(chr((number >> 16) mod 256)) &
         str(chr((number >>  8) mod 256)) &
         str(chr( number        mod 256));


(**
 *  Convert a [[string]] of bytes (interpreted as little-endian) to an [[integer]].
 *  @return an integer created from the little-endian bytes.
 *  @exception RANGE_ERROR When characters beyond '\255\' are present.
 *)
const func integer: bytesLe2Int (in string: byteStri) is func
  result
    var integer: number is 0;
  local
    var char: ch is ' ';
    var integer: shift is 0;
  begin
    for ch range byteStri do
      if ch > '\255\' then
        raise RANGE_ERROR;
      end if;
      number +:= ord(ch) << shift;
      shift +:= 8;
    end for;
  end func;


(**
 *  Convert a [[string]] of bytes (interpreted as big-endian) to an [[integer]].
 *  @return an integer created from the big-endian bytes.
 *  @exception RANGE_ERROR When characters beyond '\255\' are present.
 *)
const func integer: bytesBe2Int (in string: byteStri) is func
  result
    var integer: number is 0;
  local
    var char: ch is ' ';
  begin
    for ch range byteStri do
      if ch > '\255\' then
        raise RANGE_ERROR;
      end if;
      number <<:= 8;
      number +:= ord(ch);
    end for;
  end func;


(**
 *  Convert an [[integer]] into a little-endian [[bstring]].
 *  The result uses a twos-complement representation with a base of 256.
 *  For a negative ''number'' the most significant byte of the result
 *  (the last byte) has an ordinal >= 128.
 *  @return a bstring with the little-endian representation.
 *)
const func bstring: bStriLe (in integer: number)              is action "INT_TO_BSTRI_LE";


(**
 *  Convert an [[integer]] into a little-endian [[string]].
 *  The result uses a twos-complement representation with a base of 256.
 *  The result contains chars (bytes) with an ordinal <= 255.
 *  For a negative ''number'' the most significant byte of the result
 *  (the last byte) has an ordinal >= 128.
 *  @return a string with the little-endian representation.
 *)
const func string: bytesLe (in integer: number) is
  return str(bStriLe(number));


(**
 *  Convert an [[integer]] into a big-endian [[bstring]].
 *  The result uses a twos-complement representation with a base of 256.
 *  For a negative ''number'' the most significant byte of the result
 *  (the first byte) has an ordinal >= 128.
 *  @return a bstring with the big-endian representation.
 *)
const func bstring: bStriBe (in integer: number)              is action "INT_TO_BSTRI_BE";


(**
 *  Convert an [[integer]] into a big-endian [[string]].
 *  The result uses a twos-complement representation with a base of 256.
 *  The result contains chars (bytes) with an ordinal <= 255.
 *  For a negative ''number'' the most significant byte of the result
 *  (the first byte) has an ordinal >= 128.
 *  @return a string with the big-endian representation.
 *)
const func string: bytesBe (in integer: number) is
  return str(bStriBe(number));


(**
 *  Convert a [[bstring]] (interpreted as little-endian) to a [[bigint|bigInteger]].
 *  The ''bstri'' is interpreted as twos-complement representation
 *  with a base of 256. The result is negative when the most significant
 *  byte (the last byte) has an ordinal >= 128.
 *  @param bstri Bstring that contains the data to be converted.
 *  @return a signed bigInteger created from the little-endian bytes.
 *)
const func bigInteger: bStriLe2BigInt (in bstring: bstri)     is action "BIG_FROM_BSTRI_LE";


(**
 *  Convert a [[string]] of bytes (interpreted as little-endian) to a [[bigint|bigInteger]].
 *  The ''byteStri'' is interpreted as twos-complement representation
 *  with a base of 256. The result is negative when the most significant
 *  byte (the last byte) has an ordinal >= 128.
 *  @return a signed bigInteger created from the little-endian bytes.
 *  @exception RANGE_ERROR When characters beyond '\255\' are present.
 *)
const func bigInteger: bytesLe2BigInt (in string: byteStri) is
  return bStriLe2BigInt(bstring parse byteStri);


(**
 *  Convert a [[bstring]] (interpreted as big-endian) to a [[bigint|bigInteger]].
 *  The ''bstri'' is interpreted as twos-complement representation
 *  with a base of 256. The result is negative when the most significant
 *  byte (the first byte) has an ordinal >= 128.
 *  @param bstri Bstring that contains the data to be converted.
 *  @return a signed bigInteger created from the big-endian bytes.
 *)
const func bigInteger: bStriBe2BigInt (in bstring: bstri)     is action "BIG_FROM_BSTRI_BE";


(**
 *  Convert a [[string]] of bytes (interpreted as big-endian) to a [[bigint|bigInteger]].
 *  The ''byteStri'' is interpreted as twos-complement representation
 *  with a base of 256. The result is negative when the most significant
 *  byte (the first byte) has an ordinal >= 128.
 *  @return a signed bigInteger created from the big-endian bytes.
 *  @exception RANGE_ERROR When characters beyond '\255\' are present.
 *)
const func bigInteger: bytesBe2BigInt (in string: byteStri) is
  return bStriBe2BigInt(bstring parse byteStri);


(**
 *  Convert a [[bigint|bigInteger]] into a little-endian [[bstring]].
 *  The result uses a twos-complement representation with a base of 256.
 *  For a negative ''number'' the most significant byte of the result
 *  (the last byte) has an ordinal >= 128.
 *  @return a bstring with the little-endian representation.
 *)
const func bstring: bStriLe (in bigInteger: number)          is action "BIG_TO_BSTRI_LE";


(**
 *  Convert a [[bigint|bigInteger]] into a little-endian [[string]].
 *  The result uses a twos-complement representation with a base of 256.
 *  The result contains chars (bytes) with an ordinal <= 255.
 *  For a negative ''number'' the most significant byte of the result
 *  (the last byte) has an ordinal >= 128.
 *  @return a string with the little-endian representation.
 *)
const func string: bytesLe (in bigInteger: number) is
  return str(bStriLe(number));


(**
 *  Convert a [[bigint|bigInteger]] into a big-endian [[bstring]].
 *  The result uses a twos-complement representation with a base of 256.
 *  For a negative ''number'' the most significant byte of the result
 *  (the first byte) has an ordinal >= 128.
 *  @return a bstring with the big-endian representation.
 *)
const func bstring: bStriBe (in bigInteger: number)          is action "BIG_TO_BSTRI_BE";


(**
 *  Convert a [[bigint|bigInteger]] into a big-endian [[string]].
 *  The result uses a twos-complement representation with a base of 256.
 *  The result contains chars (bytes) with an ordinal <= 255.
 *  For a negative ''number'' the most significant byte of the result
 *  (the first byte) has an ordinal >= 128.
 *  @return a string with the big-endian representation.
 *)
const func string: bytesBe (in bigInteger: number) is
  return str(bStriBe(number));


(**
 *  Read two bytes from a file and return their little-endian value.
 *)
const func integer: getUInt16Le (inout file: in_file) is func
  result
    var integer: number is 0;
  local
    var string: stri is "";
  begin
    stri := gets(in_file, 2);
    number := ord(stri[1]) +
              ord(stri[2]) * 256;
  end func;


(**
 *  Read four bytes from a file and return their little-endian value.
 *)
const func integer: getUInt32Le (inout file: in_file) is func
  result
    var integer: number is 0;
  local
    var string: stri is "";
  begin
    stri := gets(in_file, 4);
    number := ord(stri[1]) +
              ord(stri[2]) * 256 +
              ord(stri[3]) * 65536 +
              ord(stri[4]) * 16777216;
  end func;


(**
 *  Read two bytes from a file and return their big-endian value.
 *)
const func integer: getUInt16Be (inout file: in_file) is func
  result
    var integer: number is 0;
  local
    var string: stri is "";
  begin
    stri := gets(in_file, 2);
    number := ord(stri[1]) * 256 +
              ord(stri[2]);
  end func;


(**
 *  Read four bytes from a file and return their big-endian value.
 *)
const func integer: getUInt32Be (inout file: in_file) is func
  result
    var integer: number is 0;
  local
    var string: stri is "";
  begin
    stri := gets(in_file, 4);
    number := ord(stri[1]) * 16777216 +
              ord(stri[2]) * 65536 +
              ord(stri[3]) * 256 +
              ord(stri[4]);
  end func;
