Skip to content

Instantly share code, notes, and snippets.

@jocelyn
Last active October 7, 2016 12:20
Show Gist options
  • Select an option

  • Save jocelyn/855f400f2d466382902d5cb81cec94ab to your computer and use it in GitHub Desktop.

Select an option

Save jocelyn/855f400f2d466382902d5cb81cec94ab to your computer and use it in GitHub Desktop.
Proposition to have a byte array solution within Eiffel libs
note
description: "[
Representation for byte array (item value between 0 and 255).
Could be useful in protocol, encoding, ... computations.
It provides useful representations as hex, bin, and also generic base N representation ...
]"
date: "$Date$"
revision: "$Revision$"
class
BYTE_ARRAY
inherit
ITERABLE [NATURAL_8]
READABLE_INDEXABLE [NATURAL_8]
rename
upper as count
end
create
make,
make_from_string,
make_from_hex_string,
make_from_bin_string,
make_from_base_n_string
feature {NONE} -- Initialization
make (nb: INTEGER)
-- Create current byte array with a capacity of `nb' items.
do
create string.make (nb)
end
make_from_string (s: READABLE_STRING_8)
-- Create current byte array from string, i.e an array of character 8 values, i.e from 0 to 255.
do
string := s
end
make_from_hex_string (hex: READABLE_STRING_8)
-- Create Current byte array from hexadecimal representation of byte array `hex'.
-- each item is represented by 2 hexadecimal characters, that represents a byte from 0 to 255
-- i.e from 00 to FF.
do
make_from_base_n_string (hex, 16, 2)
end
make_from_bin_string (a_bin_string: READABLE_STRING_8)
-- Create Current byte array from binary representation of byte array `a_bin_string'.
-- each item is represented by 8 bin characters, for a value between 0 to 255
-- i.e from 0000 0000 to 1111 1111.
local
i,j,n: INTEGER
c: CHARACTER_8
s: STRING
f, v: NATURAL_8
do
n := a_bin_string.count
create s.make (n // 8)
string := s
from
i := n
until
i <= 0
loop
v := 0
from
j := 1
f := 1
until
j > 8 or i < 1
loop
c := a_bin_string [i]
if c = '1' then
v := v + f -- * 1
end
f := f * 2 -- base = 2
j := j + 1
i := i - 1
end
s.prepend_character (v.to_character_8)
end
end
make_from_base_n_string (a_base_string: READABLE_STRING_8; a_base: NATURAL_8; a_char_len: INTEGER)
-- Create Current byte array from base representation of byte array `a_base_string'.
-- each item is represented by `a_char_len' characters, for a value between 0 to 255
require
char_len_big_enough: a_char_len >= number_of_characters_required_for_base_n_representation (255, a_base)
local
i,j,n: INTEGER
c: CHARACTER_8
s: STRING
n8, v: NATURAL_8
f: NATURAL_8
do
n := a_base_string.count
create s.make (n // a_char_len)
string := s
from
i := n
until
i <= 0
loop
v := 0
from
j := 1
f := 1
until
j > a_char_len or i < 1
loop
c := a_base_string [i]
n8 := base_n_character_to_natural (c)
v := v + n8 * f
f := f * a_base
j := j + 1
i := i - 1
end
s.append_character (v.to_character_8)
end
end
feature -- Access
lower: INTEGER = 1
-- Lower index;
count: INTEGER
-- Count of items.
-- (also knows as upper).
do
Result := string.count
end
natural_8_item, item alias "[]" (i: INTEGER): NATURAL_8
-- Item at index `i' represented as natural value.
do
Result := string.item_code (i).as_natural_8
end
string: STRING_8
-- String representation of Current byte array.
-- Each natural 8 item is represented by the associated character with same code.
feature -- Element change
prepend (a_byte: NATURAL_8)
-- Prepend `a_byte' to Current byte array.
do
string.prepend_character (a_byte.to_character_8)
end
extend (a_byte: NATURAL_8)
-- Extend `a_byte' to Curren byte array.
do
string.append_character (a_byte.to_character_8)
end
insert_byte (a_byte: NATURAL_8; i: INTEGER)
-- Insert `a_byte' at index `i', shifting bytes between ranks
-- `i' and `count' rightwards.
require
valid_insertion_index: 1 <= i and i <= count + 1
do
string.insert_character (a_byte.to_character_8, i)
end
remove (i: INTEGER)
-- Remove `i'-th byte.
require
valid_index: valid_index (i)
do
string.remove (i)
end
remove_sub_array (start_index, end_index: INTEGER)
-- Remove all bytes from `start_index' to `end_index' inclusive.
require
valid_start_index: 1 <= start_index
valid_end_index: end_index <= count
meaningful_interval: start_index <= end_index + 1
do
string.remove_substring (start_index, end_index)
end
append (a_byte_array: BYTE_ARRAY)
do
across
a_byte_array as ic
loop
extend (ic.item)
end
end
sub_array (start_index, end_index: INTEGER): like Current
require
valid_start_index: 1 <= start_index
valid_end_index: end_index <= count
meaningful_interval: start_index <= end_index + 1
do
create Result.make_from_string (string.substring (start_index, end_index))
end
feature -- Access and item representations.
integer_8_item (i: INTEGER): INTEGER_8
-- Item at index `i' represented as integer value from -128 to 127.
require
valid_index: valid_index (i)
local
n8: like natural_8_item
do
n8 := natural_8_item (i)
if n8 >= 128 then
Result := (-256 + n8.as_integer_32).to_integer_8
else
Result := n8.to_integer_8
end
end
base_n_item (i: INTEGER; a_base: NATURAL_8; a_char_len: INTEGER): STRING_8
-- Item at index `i' represented with base `a_base' and using `a_char_len' characters.
-- Support base from 2 to 10 (bin to decimal).
require
valid_index: valid_index (i)
accepted_base: 2 <= a_base and then a_base <= 36
char_len_big_enough: a_char_len >= number_of_characters_required_for_base_n_representation (255, a_base)
local
n8: NATURAL_8
q8,r8: NATURAL_8
do
n8 := natural_8_item (i)
create Result.make (a_char_len)
from
until
n8 = 0
loop
if n8 < a_base then
prepend_based_natural_8_to (n8, a_base, Result)
n8 := 0
else
q8 := n8 // a_base
r8 := n8 - q8 * a_base
prepend_based_natural_8_to (r8, a_base, Result)
n8 := q8
end
end
if Result.count < a_char_len then
from
until
Result.count >= a_char_len
loop
Result.prepend_character ('0')
end
end
end
hex_item (i: INTEGER): STRING_8
-- Item at index `i' represented as hexadecimal value.
require
valid_index: valid_index (i)
do
Result := item (i).to_hex_string
if Result.count = 1 then
Result.prepend_character ('0')
end
end
oct_item (i: INTEGER): STRING_8
-- Item at index `i' represented as octal value on 3 characters.
require
valid_index: valid_index (i)
do
Result := base_n_item (i, 8, 3)
end
bin_item (i: INTEGER): STRING_8
-- Item at index `i' represented as binary value on 8 characters.
require
valid_index: valid_index (i)
local
n8: NATURAL_8
j,v: INTEGER
do
n8 := natural_8_item (i)
create Result.make_filled ('0', 8)
from
j := 8
v := 128 -- 2 ^ 7
until
j = 0
loop
if n8 & v = v then
Result [8 - j + 1] := '1'
else
Result [8 - j + 1] := '0'
end
v := v // 2
j := j - 1
end
end
feature -- Status report
valid_index (i: INTEGER): BOOLEAN
-- Is `i' a valid index?
do
Result := 1 <= i and i <= count
end
feature -- Conversion
to_integer_8_array: ARRAY [INTEGER_8]
-- Integer 8 array representation of Current.
-- following format of `integer_8_item'.
local
i,n: INTEGER
do
from
i := 1
n := count
create Result.make_filled ({INTEGER_8} 0, i, n)
until
i > n
loop
Result[i] := integer_8_item (i)
i := i + 1
end
end
feature -- Conversion settings
is_lower_case: BOOLEAN
-- Using lower case of string generation (default).
do
Result := not is_upper_case
end
is_upper_case: BOOLEAN
-- Using upper case of string generation.
string_output_item_separator: detachable READABLE_STRING_8
-- Optional item separator in string output representations.
feature -- Conversion settings change
use_lower_case
-- String generation is using lowercase letters.
-- relevant for encoding with base > 10.
-- Default.
do
is_upper_case := False
end
use_upper_case
-- String generation is using uppercase letters.
-- relevant for encoding with base > 10.
do
is_upper_case := True
end
set_string_output_item_separator (s: detachable READABLE_STRING_8)
-- Set optional item separator in string output representations `string_output_item_separator' to `s'.
do
string_output_item_separator := s
end
feature -- Conversion to string
append_hex_string_to (a_string: STRING_GENERAL)
-- Append to `a_string' the byte array representation with sequence of 2 hexadecimal characters.
local
i,n: INTEGER
s: like string_output_item_separator
do
s := string_output_item_separator
from
i := 1
n := count
until
i > n
loop
if s /= Void and i > 1 then
a_string.append (s)
end
a_string.append (hex_item (i))
i := i + 1
end
end
append_dec_string_to (a_string: STRING_GENERAL)
-- Append to `a_string' the byte array representation with sequence of 3 decimal characters.
do
append_base_n_string_to (a_string, 10, 3)
end
append_oct_string_to (a_string: STRING_GENERAL)
-- Append to `a_string' the byte array representation with sequence of 3 octal characters.
do
append_base_n_string_to (a_string, 8, 3)
end
append_bin_string_to (a_string: STRING_GENERAL)
-- Append to `a_string' the byte array representation with sequence of 8 binary characters.
local
i,n: INTEGER
s: like string_output_item_separator
do
s := string_output_item_separator
from
i := 1
n := count
until
i > n
loop
if s /= Void and i > 1 then
a_string.append (s)
end
a_string.append (bin_item (i))
i := i + 1
end
end
append_base_n_string_to (a_string: STRING_GENERAL; a_base: NATURAL_8; a_char_len: INTEGER)
-- Append to `a_string' the byte array representation with sequence of `a_char_len' characters formatted with base `a_base'.
require
char_len_big_enough: a_char_len >= number_of_characters_required_for_base_n_representation (255, a_base)
accepted_base: 2 <= a_base and then a_base <= 36
local
i,n: INTEGER
s: like string_output_item_separator
do
s := string_output_item_separator
from
i := 1
n := count
until
i > n
loop
if s /= Void and i > 1 then
a_string.append (s)
end
a_string.append (base_n_item (i, a_base, a_char_len))
i := i + 1
end
end
to_hex_string: STRING_8
-- Byte array representation with sequence of 2 hexadecimal characters.
do
create Result.make (2 * count)
append_hex_string_to (Result)
end
to_dec_string: STRING_8
-- Byte array representation with sequence of 3 decimal characters.
do
Result := to_base_n_string (10, 3)
end
to_oct_string: STRING_8
-- Byte array representation with sequence of 3 octal characters.
do
Result := to_base_n_string (8, 3)
end
to_bin_string: STRING_8
-- Byte array representation with sequence of 8 binary characters.
do
create Result.make (2 * count)
append_bin_string_to (Result)
ensure
across Result as ic all ic.item = '0' or ic.item = '1' or (attached string_output_item_separator as s and then s.has (ic.item)) end
end
to_base_n_string (a_base: NATURAL_8; a_char_len: INTEGER): STRING_8
-- Byte array representation with sequence of `a_char_len' characters formatted with base `a_base'.
require
char_len_big_enough: a_char_len >= number_of_characters_required_for_base_n_representation (255, a_base)
accepted_base: 2 <= a_base and then a_base <= 36
do
create Result.make (a_char_len * count)
append_base_n_string_to (Result, a_base, a_char_len)
end
to_base_n_string_with_separator (a_base: NATURAL_8; a_char_len: INTEGER; sep: READABLE_STRING_8): STRING_8
-- Byte array representation with sequence of `a_char_len' characters formatted with base `a_base'.
require
char_len_big_enough: a_char_len >= number_of_characters_required_for_base_n_representation (255, a_base)
accepted_base: 2 <= a_base and then a_base <= 36
local
l_old_sep: like string_output_item_separator
do
l_old_sep := string_output_item_separator
set_string_output_item_separator (sep)
Result := to_base_n_string (a_base, a_char_len)
set_string_output_item_separator (l_old_sep)
end
feature -- Helpers
number_of_characters_required_for_base_n_representation (a_value: NATURAL_8; a_base: NATURAL_8): NATURAL_8
-- Number of character needed to represent `a_value' using base N `a_base'.
local
v: NATURAL_8
do
from
v := a_value
until
v = 0
loop
v := v // a_base
Result := Result + 1
end
end
feature {NONE} -- Implementation
prepend_based_natural_8_to (a_value: NATURAL_8; a_base: NATURAL_8; a_string: STRING)
-- Prepend `a_value' to a_string using `a_base' encoding.
require
valid_value: a_value < a_base
accepted_base: 2 <= a_base and then a_base <= 36
do
if a_value < 10 or a_base <= 10 then
a_string.prepend (a_value.out)
elseif is_lower_case then
a_string.prepend_character ('a' + a_value - 10)
else
check is_upper_case end
a_string.prepend_character ('A' + a_value - 10)
end
end
base_n_character_to_natural (c: CHARACTER_8): NATURAL_8
-- Natural 8 value for base n encoded character `c'.
do
inspect c
when '0' then
Result := {NATURAL_8} 0
when '1' then
Result := {NATURAL_8} 1
when '2' then
Result := {NATURAL_8} 2
when '3' then
Result := {NATURAL_8} 3
when '4' then
Result := {NATURAL_8} 4
when '5' then
Result := {NATURAL_8} 5
when '6' then
Result := {NATURAL_8} 6
when '7' then
Result := {NATURAL_8} 7
when '8' then
Result := {NATURAL_8} 8
when '9' then
Result := {NATURAL_8} 9
else
if 'a' <= c and c <= 'z' then
Result := ((c.code - ('a').code) + 10).as_natural_8
elseif 'A' <= c and c <= 'Z' then
Result := ((c.code - ('A').code) + 10).as_natural_8
else
check is_hexa: False end
end
end
end
end
class TEST_BYTE_ARRAY
create
make
feature -- Initialization
make
do
test
end
test
local
ba, ba2: BYTE_ARRAY
do
create ba.make_from_hex_string ("ABCDEFABCDEF")
print ("HEX " + ba.to_hex_string + "%N")
print ("DEC " + ba.to_dec_string + "%N")
print ("OCT " + ba.to_oct_string + "%N")
print ("BIN " + ba.to_bin_string + "%N")
print ("Base2 " + ba.to_base_n_string_with_separator (2, 8, ",") + "%N")
print ("Base8 " + ba.to_base_n_string_with_separator (8, 3, ",") + "%N")
print ("Base10 " + ba.to_base_n_string_with_separator (10, 3, ",") + "%N")
create ba2.make_from_base_n_string (ba.to_base_n_string (10, 6), 10, 6)
create ba2.make_from_bin_string (ba.to_bin_string)
check ba2.to_hex_string.same_string (ba.to_hex_string) end
print ("#Base2 " + ba2.to_base_n_string_with_separator (2, 8, ":") + "%N")
print ("Base2 " + ba.to_base_n_string_with_separator (2, 8, ":") + "%N")
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment