(********************************************************************) (* *) (* leb128.s7i Convert integers to and from LEB128 encoding. *) (* Copyright (C) 2019 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. *) (* *) (********************************************************************) (** * Read a LEB128 encoded number from an ''inFile''. * @param inFile File from which the LEB128 encoded number is read. * @return the LEB128 encoded number as [[string]]. * @exception RANGE_ERROR If characters beyond '\255;' are present. *) const func string: getLeb128 (inout file: inFile) is func result var string: resultStri is ""; local var char: ch is ' '; begin ch := getc(inFile); while ch >= '\128;' and ch <= '\255;' do resultStri &:= ch; ch := getc(inFile); end while; if ch >= '\0;' and ch <= '\127;' then resultStri &:= ch; else raise RANGE_ERROR; end if; end func; (** * Decode a LEB128 encoded [[integer]] from a [[string]]. * @param stri String with an LEB128 encoded integer that starts at * position ''pos''. * @param pos When the function is called the LEB128 number starts at ''pos''. * When the function is left ''pos'' refers to the character * after the LEB128 number. * @return the decoded LEB128 number. * @exception RANGE_ERROR If characters beyond '\255;' are present. * @exception OVERFLOW_ERROR If the result value cannot be represented * with an integer. *) const func integer: leb128ToInt (in string: stri, inout integer: pos) is func result var integer: anInt is 0; local var char: ch is ' '; var integer: lshift is 0; begin ch := stri[pos]; while ch >= '\128;' and ch <= '\255;' do anInt +:= (ord(ch) - 128) << lshift; lshift +:= 7; incr(pos); ch := stri[pos]; end while; if ch >= '\0;' and ch <= '\63;' then anInt +:= ord(ch) << lshift; elsif ch >= '\64;' and ch <= '\127;' then if lshift = 0 then anInt := ord(ch) - 128; else anInt := anInt + ((-2) << pred(lshift)); anInt +:= (ord(ch) - 127) << lshift; end if; else raise RANGE_ERROR; end if; incr(pos); end func; (** * Decode a LEB128 encoded [[integer]] from a [[string]]. * @param stri String that starts with an LEB128 encoded integer. * @return the decoded LEB128 number. * @exception RANGE_ERROR If characters beyond '\255;' are present. * @exception OVERFLOW_ERROR If the result value cannot be represented * with an integer. *) const func integer: leb128ToInt (in string: stri) is func result var integer: anInt is 0; local var integer: pos is 1; begin anInt := leb128ToInt(stri, pos); end func; (** * Decode an unsigned LEB128 encoded [[integer]] from a [[string]]. * @param stri String with an unsigned LEB128 encoded integer that starts at * position ''pos''.. * @param pos When the function is called the LEB128 number starts at ''pos''. * When the function is left ''pos'' refers to the character * after the LEB128 number. * @return the decoded unsigned LEB128 number. * @exception RANGE_ERROR If characters beyond '\255;' are present. * @exception OVERFLOW_ERROR If the result value cannot be represented * with an integer. *) const func integer: uLeb128ToInt (in string: stri, inout integer: pos) is func result var integer: anInt is 0; local var char: ch is ' '; var integer: lshift is 0; begin ch := stri[pos]; while ch >= '\128;' and ch <= '\255;' do anInt +:= (ord(ch) - 128) << lshift; lshift +:= 7; incr(pos); ch := stri[pos]; end while; if ch >= '\0;' and ch <= '\127;' then anInt +:= ord(ch) << lshift; else raise RANGE_ERROR; end if; incr(pos); end func; (** * Decode an unsigned LEB128 encoded [[integer]] from a [[string]]. * @param stri String that starts with an unsigned LEB128 encoded integer. * @return the decoded unsigned LEB128 number. * @exception RANGE_ERROR If characters beyond '\255;' are present. * @exception OVERFLOW_ERROR If the result value cannot be represented * with an integer. *) const func integer: uLeb128ToInt (in string: stri) is func result var integer: anInt is 0; local var integer: pos is 1; begin anInt := uLeb128ToInt(stri, pos); end func; (** * Encode an [[integer]] with LEB128 encoding. * @param number The number to be encoded. * @return the LEB128 encoded number as [[string]]. *) const func string: leb128 (in var integer: number) is func result var string: leb128 is ""; local var boolean: more is TRUE; var integer: aByte is 0; begin while more do aByte := number mod 128; number >>:= 7; # Sign bit of aByte is second high order bit (16#40) */ if (number = 0 and aByte < 16#40) or (number = -1 and aByte >= 16#40) then more := FALSE; else aByte +:= 128; # Set high order bit end if; leb128 &:= char(aByte); end while; end func; (** * Encode an [[integer]] with unsigned LEB128 encoding. * @param number The number to be encoded. * @return the unsigned LEB128 encoded number as [[string]]. *) const func string: uLeb128 (in var integer: number) is func result var string: uLeb128 is ""; local var integer: aByte is 0; begin if number < 0 then raise RANGE_ERROR; else repeat aByte := number mod 128; number >>:= 7; if number <> 0 then aByte +:= 128; # Set high order bit end if; uLeb128 &:= char(aByte); until number = 0; end if; end func;