1:- module(canny_endian, [byte//1, big_endian//2, little_endian//2]).

Big- and little-endian grammars

The endian predicates unify big- and little-endian words, longs and long words with lists of octets by applying shifts and masks to correctly align integer values with their endian-specific octet positions. They utilise integer-relational finite domain CLP(FD) predicates in order to implement forward and reverse translation between octets and integers.

Use of CLP allows the DCG clauses to express the integer relations between octets and their integer interpretations implicitly. The constraints simultaneously define a byte in terms of an octet and vice versa.

/

   19:- use_module(library(clpfd)).
 byte(?Byte:integer)// is semidet
Parses or generates an octet for Byte. Bytes are eight bits wide and unify with octets between 0 and 255 inclusive. Fails for octets falling outside this valid range.
Arguments:
Byte- value of octet.
   29byte(Byte) -->
   30    { Byte #= Octet /\ 0xff,
   31      Octet #= Byte /\ 0xff
   32      % Unifies a byte with an octet and vice versa. Succeeds only when the
   33      % octet fits within eight bits and so also the byte. Octets outside the
   34      % range 0 through 255 inclusive fail to match the byte size requirements.
   35    },
   36    [Octet].
 big_endian(?Width:integer, ?Word:integer)// is semidet
Unifies big-endian words with octets.

Example as follows: four octets to one big-endian 32-bit word.

?- phrase(big_endian(32, A), [4, 3, 2, 1]),
   format('~16r~n', [A]).
4030201
   48big_endian(16, Word16) -->
   49    { high_low(16, High, Low, Word16)
   50    },
   51    byte(High),
   52    byte(Low).
   53big_endian(32, Word32) -->
   54    { high_low(32, High, Low, Word32)
   55    },
   56    big_endian(16, High),
   57    big_endian(16, Low).
   58big_endian(64, Word64) -->
   59    { high_low(64, High, Low, Word64)
   60    },
   61    big_endian(32, High),
   62    big_endian(32, Low).
 little_endian(?Width:integer, ?Word:integer)// is semidet
Unifies little-endian words with octet stream.
   68little_endian(16, Word16) -->
   69    { high_low(16, High, Low, Word16)
   70    },
   71    byte(Low),
   72    byte(High).
   73little_endian(32, Word32) -->
   74    { high_low(32, High, Low, Word32)
   75    },
   76    little_endian(16, Low),
   77    little_endian(16, High).
   78little_endian(64, Word64) -->
   79    { high_low(64, High, Low, Word64)
   80    },
   81    little_endian(32, Low),
   82    little_endian(32, High).
 high_low(?Width:integer, ?High:integer, ?Low:integer, ?Word:integer) is semidet
Unifies High and Low with Word over Width bits.

Matches High and Low with Word depending on endian-order. Big-endian words map to octets where the most-significant precede the least significant. Little-endian words reverse this ordering. By convention, many network-oriented octet streams prefer big-endian because it reads more naturally when you dump the octets in progress order.

Arguments:
Width- of Word: 16, 32 or 64 bits.
High- half of Word.
Low- half of Word.
Word- value of one or more octets.
  103high_low(16, High, Low, Word16) :-
  104    Low #= Word16 /\ 0xff,
  105    High #= (Word16 // (1 << 8)) /\ 0xff,
  106    Word16 #= (High << 8) \/ Low.
  107high_low(32, High, Low, Word32) :-
  108    Low #= Word32 /\ 0xffff,
  109    High #= (Word32 // (1 << 16)) /\ 0xffff,
  110    Word32 #= (High << 16) \/ Low.
  111high_low(64, High, Low, Word64) :-
  112    Low #= Word64 /\ 0xffff_ffff,
  113    High #= (Word64 // (1 << 32)) /\ 0xffff_ffff,
  114    Word64 #= (High << 32) \/ Low