...
Run Format

Source file src/encoding/hex/hex.go

  // Copyright 2009 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  // Package hex implements hexadecimal encoding and decoding.
  package hex
  
  import (
  	"bytes"
  	"errors"
  	"fmt"
  	"io"
  )
  
  var hextable = [16]byte{
  	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  	'a', 'b', 'c', 'd', 'e', 'f',
  }
  
  // EncodedLen returns the length of an encoding of n source bytes.
  // Specifically, it returns n * 2.
  func EncodedLen(n int) int { return n * 2 }
  
  // Encode encodes src into EncodedLen(len(src))
  // bytes of dst. As a convenience, it returns the number
  // of bytes written to dst, but this value is always EncodedLen(len(src)).
  // Encode implements hexadecimal encoding.
  func Encode(dst, src []byte) int {
  	for i, v := range src {
  		dst[i*2] = hextable[v>>4]
  		dst[i*2+1] = hextable[v&0x0f]
  	}
  
  	return len(src) * 2
  }
  
  // ErrLength results from decoding an odd length slice.
  var ErrLength = errors.New("encoding/hex: odd length hex string")
  
  // InvalidByteError values describe errors resulting from an invalid byte in a hex string.
  type InvalidByteError byte
  
  func (e InvalidByteError) Error() string {
  	return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e))
  }
  
  // DecodedLen returns the length of a decoding of x source bytes.
  // Specifically, it returns x / 2.
  func DecodedLen(x int) int { return x / 2 }
  
  // Decode decodes src into DecodedLen(len(src)) bytes,
  // returning the actual number of bytes written to dst.
  //
  // Decode expects that src contain only hexadecimal
  // characters and that src should have an even length.
  func Decode(dst, src []byte) (int, error) {
  	if len(src)%2 == 1 {
  		return 0, ErrLength
  	}
  
  	for i := 0; i < len(src)/2; i++ {
  		a, ok := fromHexChar(src[i*2])
  		if !ok {
  			return 0, InvalidByteError(src[i*2])
  		}
  		b, ok := fromHexChar(src[i*2+1])
  		if !ok {
  			return 0, InvalidByteError(src[i*2+1])
  		}
  		dst[i] = (a << 4) | b
  	}
  
  	return len(src) / 2, nil
  }
  
  // fromHexChar converts a hex character into its value and a success flag.
  func fromHexChar(c byte) (byte, bool) {
  	switch {
  	case '0' <= c && c <= '9':
  		return c - '0', true
  	case 'a' <= c && c <= 'f':
  		return c - 'a' + 10, true
  	case 'A' <= c && c <= 'F':
  		return c - 'A' + 10, true
  	}
  
  	return 0, false
  }
  
  // EncodeToString returns the hexadecimal encoding of src.
  func EncodeToString(src []byte) string {
  	dst := make([]byte, EncodedLen(len(src)))
  	Encode(dst, src)
  	return string(dst)
  }
  
  // DecodeString returns the bytes represented by the hexadecimal string s.
  func DecodeString(s string) ([]byte, error) {
  	src := []byte(s)
  	dst := make([]byte, DecodedLen(len(src)))
  	_, err := Decode(dst, src)
  	if err != nil {
  		return nil, err
  	}
  	return dst, nil
  }
  
  // Dump returns a string that contains a hex dump of the given data. The format
  // of the hex dump matches the output of `hexdump -C` on the command line.
  func Dump(data []byte) string {
  	var buf bytes.Buffer
  	dumper := Dumper(&buf)
  	dumper.Write(data)
  	dumper.Close()
  	return buf.String()
  }
  
  // Dumper returns a WriteCloser that writes a hex dump of all written data to
  // w. The format of the dump matches the output of `hexdump -C` on the command
  // line.
  func Dumper(w io.Writer) io.WriteCloser {
  	return &dumper{w: w}
  }
  
  type dumper struct {
  	w          io.Writer
  	rightChars [18]byte
  	buf        [14]byte
  	used       int  // number of bytes in the current line
  	n          uint // number of bytes, total
  }
  
  func toChar(b byte) byte {
  	if b < 32 || b > 126 {
  		return '.'
  	}
  	return b
  }
  
  func (h *dumper) Write(data []byte) (n int, err error) {
  	// Output lines look like:
  	// 00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=|
  	// ^ offset                          ^ extra space              ^ ASCII of line.
  	for i := range data {
  		if h.used == 0 {
  			// At the beginning of a line we print the current
  			// offset in hex.
  			h.buf[0] = byte(h.n >> 24)
  			h.buf[1] = byte(h.n >> 16)
  			h.buf[2] = byte(h.n >> 8)
  			h.buf[3] = byte(h.n)
  			Encode(h.buf[4:], h.buf[:4])
  			h.buf[12] = ' '
  			h.buf[13] = ' '
  			_, err = h.w.Write(h.buf[4:])
  			if err != nil {
  				return
  			}
  		}
  		Encode(h.buf[:], data[i:i+1])
  		h.buf[2] = ' '
  		l := 3
  		if h.used == 7 {
  			// There's an additional space after the 8th byte.
  			h.buf[3] = ' '
  			l = 4
  		} else if h.used == 15 {
  			// At the end of the line there's an extra space and
  			// the bar for the right column.
  			h.buf[3] = ' '
  			h.buf[4] = '|'
  			l = 5
  		}
  		_, err = h.w.Write(h.buf[:l])
  		if err != nil {
  			return
  		}
  		n++
  		h.rightChars[h.used] = toChar(data[i])
  		h.used++
  		h.n++
  		if h.used == 16 {
  			h.rightChars[16] = '|'
  			h.rightChars[17] = '\n'
  			_, err = h.w.Write(h.rightChars[:])
  			if err != nil {
  				return
  			}
  			h.used = 0
  		}
  	}
  	return
  }
  
  func (h *dumper) Close() (err error) {
  	// See the comments in Write() for the details of this format.
  	if h.used == 0 {
  		return
  	}
  	h.buf[0] = ' '
  	h.buf[1] = ' '
  	h.buf[2] = ' '
  	h.buf[3] = ' '
  	h.buf[4] = '|'
  	nBytes := h.used
  	for h.used < 16 {
  		l := 3
  		if h.used == 7 {
  			l = 4
  		} else if h.used == 15 {
  			l = 5
  		}
  		_, err = h.w.Write(h.buf[:l])
  		if err != nil {
  			return
  		}
  		h.used++
  	}
  	h.rightChars[nBytes] = '|'
  	h.rightChars[nBytes+1] = '\n'
  	_, err = h.w.Write(h.rightChars[:nBytes+2])
  	return
  }
  

View as plain text