ethernet-address.ss
;;  network: a library of network utilities.
;;  Copyright (C) 2006 Dave Herman
;; 
;;  Portions based on JUG (Java Uuid Generator)
;;  Copyright (c) 2002-2004 Tatu Saloranta, tatu.saloranta@iki.fi
;; 
;;  This 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.
;; 
;;  This 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 library; if not, write to the Free Software Foundation,
;;  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

(module ethernet-address mzscheme
  (require (lib "etc.ss")
           (lib "string.ss" "srfi" "13")
           (lib "string.ss"))

  ;; (vector-immutable/c byte byte byte byte byte byte)
  (define-struct ethernet-address (fields) #f)

  (define (list->ethernet-address ls)
    (unless (= (length ls) 6)
      (error 'list->ethernet-address "must be exactly 6 bytes"))
    (make-ethernet-address
     (vector->immutable-vector (list->vector ls))))

  (define bytes->ethernet-address
    (opt-lambda (bytes [start 0] [end (bytes-length bytes)])
      (unless (= (- end start) 6)
        (error 'bytes->ethernet-address "must be exactly 6 bytes"))
      (make-ethernet-address
       (vector->immutable-vector (list->vector (bytes->list (subbytes bytes start end)))))))

  (define (%02X x)
    (string-upcase (string-pad (format "~x" x) 2 #\0)))

  (define ethernet-address->string
    (opt-lambda (address [sep #\:])
      (let ([fields (vector->list (ethernet-address-fields address))])
        (string-join (map %02X fields)
                     (format "~a" sep)))))

  (define (ethernet-address->bytes address)
    (list->bytes (vector->list (ethernet-address-fields address))))

  (define string->ethernet-address
    (opt-lambda (str [sep #\:])
      (let ([pieces (regexp-split (regexp-quote (format "~a" sep)) str)])
        (unless (= (length pieces) 6)
          (error 'string->ethernet-address "invalid format"))
        (let ([ns (map (lambda (s)
                         (string->number s 16))
                       pieces)])
          (unless (andmap (lambda (x)
                            (<= #x00 x #xFF))
                          ns)
            (error 'string->ethernet-address "address field out of range"))
          (make-ethernet-address (list->vector ns))))))

  (provide ethernet-address?
           list->ethernet-address
           bytes->ethernet-address
           string->ethernet-address
           ethernet-address->bytes
           ethernet-address->string))