;;; fasl-helpers.ss
;;; Copyright 1984-2017 Cisco Systems, Inc.
;;; 
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;; 
;;; http://www.apache.org/licenses/LICENSE-2.0
;;; 
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

(module (put8 put16 put32 put64 put-iptr put-uptr)
  (define (bit-mask k) (- (ash 1 k) 1))
  (define put8
    (lambda (p n)
      (put-u8 p (fxlogand n (bit-mask 8)))))
  (define put16-le
    (cond
      [(>= (most-positive-fixnum) (bit-mask 16))
       (lambda (p n)
         (put-u8 p (fxlogand n (bit-mask 8)))
         (put-u8 p (fxlogand (fxsrl n 8) (bit-mask 8))))]
      [else ($oops 'put16-le "unsupported fixnum size")]))
  (define put16-be
    (cond
      [(>= (most-positive-fixnum) (bit-mask 16))
       (lambda (p n)
         (put-u8 p (fxlogand (fxsrl n 8) (bit-mask 8)))
         (put-u8 p (fxlogand n (bit-mask 8))))]
      [else ($oops 'put16-be "unsupported fixnum size")]))
  (define put16
    (lambda (p n)
      (constant-case native-endianness
        [(little) (put16-le p n)]
        [(big) (put16-be p n)])))
  (define put32-le
    (cond
      [(>= (most-positive-fixnum) (bit-mask 32))
       (lambda (p n)
         (put-u8 p (fxlogand n (bit-mask 8)))
         (put-u8 p (fxlogand (fxsrl n 8) (bit-mask 8)))
         (put-u8 p (fxlogand (fxsrl n 16) (bit-mask 8)))
         (put-u8 p (fxlogand (fxsrl n 24) (bit-mask 8))))]
      [(>= (most-positive-fixnum) (bit-mask 24))
       (lambda (p n)
         (cond
           [(fixnum? n)
            (put-u8 p (fxlogand n (bit-mask 8)))
            (put-u8 p (fxlogand (fxsrl n 8) (bit-mask 8)))
            (put-u8 p (fxlogand (fxsrl n 16) (bit-mask 8)))
            (put-u8 p (fxlogand (fxsra n 24) (bit-mask 8)))]
           [else
            (let ([n (logand n (bit-mask 16))])
              (put-u8 p (fxlogand n (bit-mask 8)))
              (put-u8 p (fxsrl n 8)))
            (let ([n (ash n -16)])
              (put-u8 p (fxlogand n (bit-mask 8)))
              (put-u8 p (fxlogand (fxsra n 8) (bit-mask 8))))]))]
      [else ($oops 'put32-le "unsupported fixnum size")]))
  (define put32-be
    (cond
      [(>= (most-positive-fixnum) (bit-mask 32))
       (lambda (p n)
         (put-u8 p (fxlogand (fxsrl n 24) (bit-mask 8)))
         (put-u8 p (fxlogand (fxsrl n 16) (bit-mask 8)))
         (put-u8 p (fxlogand (fxsrl n 8) (bit-mask 8)))
         (put-u8 p (fxlogand n (bit-mask 8))))]
      [(>= (most-positive-fixnum) (bit-mask 24))
       (lambda (p n)
         (cond
           [(fixnum? n)
            (put-u8 p (fxlogand (fxsra n 24) (bit-mask 8)))
            (put-u8 p (fxlogand (fxsrl n 16) (bit-mask 8)))
            (put-u8 p (fxlogand (fxsrl n 8) (bit-mask 8)))
            (put-u8 p (fxlogand n (bit-mask 8)))]
           [else
            (let ([n (ash n -16)])
              (put-u8 p (fxlogand (fxsra n 8) (bit-mask 8)))
              (put-u8 p (fxlogand n (bit-mask 8))))
            (let ([n (logand n (bit-mask 16))])
              (put-u8 p (fxsrl n 8))
              (put-u8 p (fxlogand n (bit-mask 8))))]))]
      [else ($oops 'put32-be "unsupported fixnum size")]))
  (define put32
    (lambda (p n)
      (constant-case native-endianness
        [(little) (put32-le p n)]
        [(big) (put32-be p n)])))
  (define put64-le
    (lambda (p n)
      (cond
        [(and (>= (most-positive-fixnum) (bit-mask 32)) (fixnum? n))
         (put32-le p (fxlogand n (bit-mask 32)))
         (put32-le p (ash n -32))]
        [else
         (put32-le p (logand n (bit-mask 32)))
         (put32-le p (ash n -32))])))
  (define put64-be
    (lambda (p n)
      (cond
        [(and (>= (most-positive-fixnum) (bit-mask 32)) (fixnum? n))
         (put32-be p (ash n -32))
         (put32-be p (fxlogand n (bit-mask 32)))]
        [else
         (put32-be p (ash n -32))
         (put32-be p (logand n (bit-mask 32)))])))
  (define put64
    (lambda (p n)
      (constant-case native-endianness
        [(little) (put64-le p n)]
        [(big) (put64-be p n)])))
  (define put-iptr
    (lambda (p n0)
      (let f ([n (if (< n0 0) (- n0) n0)] [cbit 0])
        (if (and (fixnum? n) (fx<= n 63))
            (put8 p (fxlogor (if (< n0 0) #x80 0) (fxsll n 1) cbit))
            (begin
              (f (ash n -7) 1)
              (put-u8 p (fxlogor (fxsll (logand n #x7f) 1) cbit)))))))
  (define put-uptr
    (lambda (p n)
      (unless (>= n 0)
        ($oops 'compiler-internal "put-uptr received negative input ~s" n))
      (let f ([n n] [cbit 0])
        (if (and (fixnum? n) (fx<= n 127))
            (put-u8 p (fxlogor (fxsll n 1) cbit))
            (begin
              (f (ash n -7) 1)
              (put-u8 p (fxlogor (fxsll (logand n #x7f) 1) cbit)))))))
)

(define emit-header
  (case-lambda
    [(p version mtype) (emit-header p version mtype '())]
    [(p version mtype bootfiles)
     (define (put-str p s)
       (let ([n (string-length s)])
         (do ([i 0 (fx+ i 1)])
             ((fx= i n))
           (let* ([c (string-ref s i)] [k (char->integer c)])
             (unless (fx<= k 255)
               ($oops #f "cannot handle bootfile name character ~s whose integer code exceeds 255" c))
             (put-u8 p k)))))
     (put-bytevector p (constant fasl-header))
     (put-uptr p version)
     (put-uptr p mtype)
     (put-u8 p (char->integer #\())           ; )
     (let f ([bootfiles bootfiles] [sep? #f])
       (unless (null? bootfiles)
         (when sep? (put-u8 p (char->integer #\space)))
         (put-str p (car bootfiles))
         (f (cdr bootfiles) #t)))        ; (
     (put-u8 p (char->integer #\)))]))
