September 2017 Update

This commit is contained in:
Ingy döt Net 2017-09-23 10:01:46 +02:00
parent bba7bfd280
commit ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions

View file

@ -19,3 +19,4 @@ nine (or more) bits long.
* Limits in the maximum number of bits that can be written/read in a single read/write operation are allowed.
* Errors handling is not mandatory
<br><br>

View file

@ -0,0 +1,140 @@
(defpackage :rosetta.bitwise-i/o
(:use :common-lisp)
(:export :bitwise-i/o-demo))
(in-package :rosetta.bitwise-i/o)
(defun byte->bit-vector (byte byte-bits)
"Convert one BYTE into a bit-vector of BYTE-BITS length."
(let ((vector (make-array byte-bits :element-type 'bit))
(bit-value 1))
(declare (optimize (speed 3)))
(dotimes (bit-index byte-bits vector)
(setf (aref vector bit-index)
(if (plusp (logand byte (the (unsigned-byte 8) bit-value)))
1 0))
(setq bit-value (ash bit-value 1)))))
(defun bytes->bit-vector (byte-vector byte-bits)
"Convert a BYTE-VECTOR into a bit-vector, with each byte taking BYTE-BITS.
For optimization's sake, I limit the size of the vector to (FLOOR
MOST-POSITIVE-FIXNUM BYTE-BITS), which is somewhat ridiculously long,
but allows the compiler to trust that indices will fit in a FIXNUM."
(reduce (lambda (a b) (concatenate 'bit-vector a b))
(map 'list (lambda (byte) (byte->bit-vector byte byte-bits)) byte-vector)))
(defun ascii-char-p (char)
"True if CHAR is an ASCII character"
(< (char-code char) #x80))
(defun assert-ascii-string (string)
"`ASSERT' that STRING is an ASCII string."
(assert (every #'ascii-char-p string)
(string)
"STRING must contain only ASCII (7-bit) characters;~%“~a”
contains non-ASCII character~p~:*: ~{~% ~c ~:* ~@c ~}"
string (coerce (remove-duplicates (remove-if #'ascii-char-p string)
:test #'char=)
'list)))
(defun ascii-string->bit-vector (string)
"Convert a STRING consisting only of characters in the ASCII \(7-bit)
range into a bit-vector of 7 bits per character.
This assumes \(as is now, in 2017, I believe universally the case) that
the local character code system \(as for `CHAR-CODE' and `CODE-CHAR') is
Unicode, or at least, a superset of ASCII \(eg: ISO-8859-*)
"
(check-type string simple-string)
(assert-ascii-string string)
(bytes->bit-vector (map 'vector #'char-code string) 7))
(defun pad-bit-vector-to-8 (vector)
"Ensure that VECTOR is a multiple of 8 bits in length."
(adjust-array vector (* 8 (ceiling (length vector) 8))))
(defun bit-vector->byte (vector)
"Convert VECTOR into a single byte."
(declare (optimize (speed 3)))
(check-type vector bit-vector)
(assert (<= (length vector) 8))
(reduce (lambda (x y)
(logior (the (unsigned-byte 8)
(ash (the (unsigned-byte 8) x) 1))
(the bit y)))
(reverse vector) :initial-value 0))
(defun bit-vector->bytes (vector byte-size &key (truncatep nil))
"Convert a bit vector VECTOR into a vector of bytes of BYTE-SIZE bits each.
If TRUNCATEP, then discard any trailing bits."
(let* ((out-length (funcall (if truncatep 'floor 'ceiling)
(length vector)
byte-size))
(output (make-array out-length
:element-type (list 'unsigned-byte byte-size))))
(loop for byte from 0 below out-length
for start-bit = 0 then end-bit
for end-bit = byte-size then (min (+ byte-size end-bit)
(length vector))
do (setf (aref output byte)
(bit-vector->byte (subseq vector start-bit end-bit))))
output))
(defun ascii-pack-to-8-bit (string)
"Pack an ASCII STRING into 8-bit bytes (7→8 bit packing)"
(bit-vector->bytes (ascii-string->bit-vector string)
8))
(defun unpack-ascii-from-8-bits (byte-vector)
"Convert an 8-bit BYTE-VECTOR into an array of (unpacked) 7-bit bytes."
(map 'string #'code-char
(bit-vector->bytes
(pad-bit-vector-to-8 (bytes->bit-vector byte-vector 8))
7
:truncatep t)))
(defun write-7->8-bit-string-to-file (string pathname)
"Given a string of 7-bit character STRING, create a new file at PATHNAME
with the contents of that string packed into 8-bit bytes."
(format *trace-output* "~&Writing string to ~a in packed 7→8 bits…~%“~a”"
pathname string)
(assert-ascii-string string)
(with-open-file (output pathname
:direction :output
:if-exists :supersede
:element-type '(unsigned-byte 8))
(write-sequence (ascii-pack-to-8-bit string) output)
(finish-output output)
(let ((expected-length (ceiling (* (length string) 7) 8)))
(assert (= (file-length output) expected-length) ()
"The file written was ~:d byte~:p in length, ~
but the string supplied should have written ~:d byte~:p."
(file-length output) expected-length))))
(defun read-file-into-byte-array (pathname)
"Read a binary file into a byte array"
(with-open-file (input pathname
:direction :input
:if-does-not-exist :error
:element-type '(unsigned-byte 8))
(let ((buffer (make-array (file-length input)
:element-type '(unsigned-byte 8))))
(read-sequence buffer input)
buffer)))
(defun read-8->7-bit-string-from-file (pathname)
"Read 8-bit packed data from PATHNAME and return it as
a 7-bit string."
(unpack-ascii-from-8-bits (read-file-into-byte-array pathname)))
(defun bitwise-i/o-demo (&key (string "Hello, World.")
(pathname #p"/tmp/demo.bin"))
"Writes STRING to PATHNAME after 78 bit packing, then reads it back
to validate."
(write-7->8-bit-string-to-file string pathname)
(let ((read-back (read-8->7-bit-string-from-file pathname)))
(assert (equal string read-back) ()
"Reading back string got:~%“~a”~%…expected:~%“~a”" read-back string)
(format *trace-output* "~&String read back matches:~%“~a”" read-back))
(finish-output *trace-output*))

View file

@ -0,0 +1,26 @@
BITWISE-I/O> (bitwise-i/o-demo)
Writing string to /tmp/demo.bin in packed 78 bits
Hello, World.
String read back matches:
Hello, World.
NIL
BITWISE-I/O> (bitwise-i/o-demo :string "It doesn't, however, do UTF-7. So, no ☠ or 🙋")
Writing string to /tmp/demo.bin in packed 78 bits
It doesn't, however, do UTF-7. So, no or 🙋
STRING must contain only ASCII (7-bit) characters;
It doesn't, however, do UTF-7. So, no or 🙋
contains non-ASCII characters:
#\SKULL_AND_CROSSBONES
🙋 #\HAPPY_PERSON_RAISING_ONE_HAND
[Condition of type SIMPLE-ERROR]
Restarts:
0: [CONTINUE] Retry assertion with new value for STRING.
1: [RETRY] Retry SLIME REPL evaluation request.
2: [*ABORT] Return to SLIME's top level.
3: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {10152E8033}>)
ABORT

View file

@ -0,0 +1,94 @@
type
BitWriter * = tuple
file: File
bits: uint8
nRemain: int
BitReader * = tuple
file: File
bits: uint8
nRemain: int
nRead: int
proc newBitWriter * (file: File) : ref BitWriter =
result = new BitWriter
result.file = file
result.bits = 0
result.nRemain = 8
proc flushBits (stream : ref BitWriter) =
discard stream.file.writeBuffer(stream.bits.addr, 1)
stream.nRemain = 8
stream.bits = 0
proc write * (stream: ref BitWriter, bits: uint8, nBits: int) =
assert(nBits <= 8)
for ii in countdown((nBits - 1), 0) :
stream.bits = (stream.bits shl 1) or ((bits shr ii) and 1)
stream.nRemain.dec(1)
if stream.nRemain == 0:
stream.flushBits
proc flush * (stream: ref BitWriter) =
if stream.nRemain < 8:
stream.bits = stream.bits shl stream.nRemain
stream.flushBits
proc newBitReader * (file: File) : ref BitReader =
result = new BitReader
result.file = file
result.bits = 0
result.nRemain = 0
result.nRead = 0
proc read * (stream: ref BitReader, nBits: int) : uint8 =
assert(nBits <= 8)
result = 0
for ii in 0 .. < nBits :
if stream.nRemain == 0:
stream.nRead = stream.file.readBuffer(stream.bits.addr, 1)
if stream.nRead == 0:
break
stream.nRemain = 8
result = (result shl 1) or ((stream.bits shr 7) and 1)
stream.bits = stream.bits shl 1
stream.nRemain.dec(1)
when isMainModule:
var
file: File
writer: ref BitWriter
reader: ref BitReader
file = open("testfile.dat", fmWrite)
writer = newBitWriter(file)
for ii in 0 .. 255:
writer.write(ii.uint8, 7)
writer.flush
file.close
var dataCtr = 0
file = open("testfile.dat", fmRead)
reader = newBitReader(file)
while true:
let aByte = reader.read(7)
if reader.nRead == 0:
break
assert((dataCtr and 0x7f).uint8 == aByte)
assert(dataCtr == 256)
file.close
echo "OK"

View file

@ -1,11 +1,11 @@
sub encode-ascii(Str $s) {
my @b = $s.ords».fmt("%07b")».comb;
my @b = flat $s.ords».fmt("%07b")».comb;
@b.push(0) until @b %% 8; # padding
Buf.new: gather while @b { take reduce * *2+*, (@b.pop for ^8) }
}
sub decode-ascii(Buf $b) {
my @b = $b.list».fmt("%08b")».comb;
my @b = flat $b.list».fmt("%08b")».comb;
@b.shift until @b %% 7; # remove padding
@b = gather while @b { take reduce * *2+*, (@b.pop for ^7) }
return [~] @b».chr;

View file

@ -0,0 +1,15 @@
/*REXX pgm encodes/decodes/displays ASCII character strings as (7─bits) binary string.*/
parse arg $; if $=='' then $='STRING' /*get optional argument; Use default ? */
say ' input string=' $ /*display the input string to terminal.*/
out=comp($); say ' encoded string=' out /*encode─► 7─bit binary string; display*/
ori=dcomp(out); say ' decoded string=' ori /*decode─► 8─bit char " ; " */
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
comp: parse arg x; z=; do j=1 for length(x) /*convert─►right-justified 7-bit binary*/
z=z || right( x2b( c2x( substr(x, j, 1) )), 7)
end /*j*/; return left(z,length(z)+(8-length(z)//8)//8,0)
/*──────────────────────────────────────────────────────────────────────────────────────*/
dcomp: parse arg x; z=; do k=1 by 7 to length(x); _=substr(x, k, 7)
if right(_, 1)==' ' then leave
z=z || x2c( b2x(0 || _) )
end /*k*/; return z

View file

@ -0,0 +1,13 @@
// stream of numBits sized ints to bytes, numBits<8
fcn toBytes(n,[(numBits,acc,bitsSoFar)]state){
acc=acc.shiftLeft(numBits) + n; bitsSoFar+=numBits;
reg r;
if(bitsSoFar>=8){
bitsSoFar-=8;
r=acc.shiftRight(bitsSoFar);
acc=acc.bitAnd((-1).shiftLeft(bitsSoFar).bitNot());
}
else r=Void.Skip; // need more bits to make a byte
state.clear(numBits,acc,bitsSoFar);
r
}

View file

@ -0,0 +1,7 @@
ns:="THIS IS A TEST".pump(List,"toAsc",'-(0x20));
ns.println(ns.len());
state:=L(6,0,0,L()); // input is six bits wide
cns:=ns.pump(List,toBytes.fp1(state)); // List could be a file or socket or ...
if(state[2]) cns+=toBytes(0,state); // flush
cns.println(cns.len());

View file

@ -0,0 +1,12 @@
// stream of bytes to numBits sized ints, 1<numBits<32
fcn fromBytes(n,[(numBits,acc,bitsSoFar,buf)]state){
acc=acc.shiftLeft(8) + n; bitsSoFar+=8;
buf.clear();
while(bitsSoFar>=numBits){
bitsSoFar-=numBits;
buf.append(acc.shiftRight(bitsSoFar));
acc=acc.bitAnd((-1).shiftLeft(bitsSoFar).bitNot());
}
state.clear(numBits,acc,bitsSoFar,buf);
return(Void.Write,Void.Write,buf); // append contents of buf to result
}

View file

@ -0,0 +1,4 @@
state:=L(6,0,0,L()); // output is six bits wide
r:=cns.pump(List,fromBytes.fp1(state)); // cns could be a file or ...
r.println(r.len());
r.pump(String,'+(0x20),"toChar").println();