Home | History | Annotate | Download | only in hex
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Package hex implements hexadecimal encoding and decoding.
      6 package hex
      7 
      8 import (
      9 	"bytes"
     10 	"errors"
     11 	"fmt"
     12 	"io"
     13 )
     14 
     15 const hextable = "0123456789abcdef"
     16 
     17 // EncodedLen returns the length of an encoding of n source bytes.
     18 func EncodedLen(n int) int { return n * 2 }
     19 
     20 // Encode encodes src into EncodedLen(len(src))
     21 // bytes of dst.  As a convenience, it returns the number
     22 // of bytes written to dst, but this value is always EncodedLen(len(src)).
     23 // Encode implements hexadecimal encoding.
     24 func Encode(dst, src []byte) int {
     25 	for i, v := range src {
     26 		dst[i*2] = hextable[v>>4]
     27 		dst[i*2+1] = hextable[v&0x0f]
     28 	}
     29 
     30 	return len(src) * 2
     31 }
     32 
     33 // ErrLength results from decoding an odd length slice.
     34 var ErrLength = errors.New("encoding/hex: odd length hex string")
     35 
     36 // InvalidByteError values describe errors resulting from an invalid byte in a hex string.
     37 type InvalidByteError byte
     38 
     39 func (e InvalidByteError) Error() string {
     40 	return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e))
     41 }
     42 
     43 func DecodedLen(x int) int { return x / 2 }
     44 
     45 // Decode decodes src into DecodedLen(len(src)) bytes, returning the actual
     46 // number of bytes written to dst.
     47 //
     48 // If Decode encounters invalid input, it returns an error describing the failure.
     49 func Decode(dst, src []byte) (int, error) {
     50 	if len(src)%2 == 1 {
     51 		return 0, ErrLength
     52 	}
     53 
     54 	for i := 0; i < len(src)/2; i++ {
     55 		a, ok := fromHexChar(src[i*2])
     56 		if !ok {
     57 			return 0, InvalidByteError(src[i*2])
     58 		}
     59 		b, ok := fromHexChar(src[i*2+1])
     60 		if !ok {
     61 			return 0, InvalidByteError(src[i*2+1])
     62 		}
     63 		dst[i] = (a << 4) | b
     64 	}
     65 
     66 	return len(src) / 2, nil
     67 }
     68 
     69 // fromHexChar converts a hex character into its value and a success flag.
     70 func fromHexChar(c byte) (byte, bool) {
     71 	switch {
     72 	case '0' <= c && c <= '9':
     73 		return c - '0', true
     74 	case 'a' <= c && c <= 'f':
     75 		return c - 'a' + 10, true
     76 	case 'A' <= c && c <= 'F':
     77 		return c - 'A' + 10, true
     78 	}
     79 
     80 	return 0, false
     81 }
     82 
     83 // EncodeToString returns the hexadecimal encoding of src.
     84 func EncodeToString(src []byte) string {
     85 	dst := make([]byte, EncodedLen(len(src)))
     86 	Encode(dst, src)
     87 	return string(dst)
     88 }
     89 
     90 // DecodeString returns the bytes represented by the hexadecimal string s.
     91 func DecodeString(s string) ([]byte, error) {
     92 	src := []byte(s)
     93 	dst := make([]byte, DecodedLen(len(src)))
     94 	_, err := Decode(dst, src)
     95 	if err != nil {
     96 		return nil, err
     97 	}
     98 	return dst, nil
     99 }
    100 
    101 // Dump returns a string that contains a hex dump of the given data. The format
    102 // of the hex dump matches the output of `hexdump -C` on the command line.
    103 func Dump(data []byte) string {
    104 	var buf bytes.Buffer
    105 	dumper := Dumper(&buf)
    106 	dumper.Write(data)
    107 	dumper.Close()
    108 	return string(buf.Bytes())
    109 }
    110 
    111 // Dumper returns a WriteCloser that writes a hex dump of all written data to
    112 // w. The format of the dump matches the output of `hexdump -C` on the command
    113 // line.
    114 func Dumper(w io.Writer) io.WriteCloser {
    115 	return &dumper{w: w}
    116 }
    117 
    118 type dumper struct {
    119 	w          io.Writer
    120 	rightChars [18]byte
    121 	buf        [14]byte
    122 	used       int  // number of bytes in the current line
    123 	n          uint // number of bytes, total
    124 }
    125 
    126 func toChar(b byte) byte {
    127 	if b < 32 || b > 126 {
    128 		return '.'
    129 	}
    130 	return b
    131 }
    132 
    133 func (h *dumper) Write(data []byte) (n int, err error) {
    134 	// Output lines look like:
    135 	// 00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=|
    136 	// ^ offset                          ^ extra space              ^ ASCII of line.
    137 	for i := range data {
    138 		if h.used == 0 {
    139 			// At the beginning of a line we print the current
    140 			// offset in hex.
    141 			h.buf[0] = byte(h.n >> 24)
    142 			h.buf[1] = byte(h.n >> 16)
    143 			h.buf[2] = byte(h.n >> 8)
    144 			h.buf[3] = byte(h.n)
    145 			Encode(h.buf[4:], h.buf[:4])
    146 			h.buf[12] = ' '
    147 			h.buf[13] = ' '
    148 			_, err = h.w.Write(h.buf[4:])
    149 			if err != nil {
    150 				return
    151 			}
    152 		}
    153 		Encode(h.buf[:], data[i:i+1])
    154 		h.buf[2] = ' '
    155 		l := 3
    156 		if h.used == 7 {
    157 			// There's an additional space after the 8th byte.
    158 			h.buf[3] = ' '
    159 			l = 4
    160 		} else if h.used == 15 {
    161 			// At the end of the line there's an extra space and
    162 			// the bar for the right column.
    163 			h.buf[3] = ' '
    164 			h.buf[4] = '|'
    165 			l = 5
    166 		}
    167 		_, err = h.w.Write(h.buf[:l])
    168 		if err != nil {
    169 			return
    170 		}
    171 		n++
    172 		h.rightChars[h.used] = toChar(data[i])
    173 		h.used++
    174 		h.n++
    175 		if h.used == 16 {
    176 			h.rightChars[16] = '|'
    177 			h.rightChars[17] = '\n'
    178 			_, err = h.w.Write(h.rightChars[:])
    179 			if err != nil {
    180 				return
    181 			}
    182 			h.used = 0
    183 		}
    184 	}
    185 	return
    186 }
    187 
    188 func (h *dumper) Close() (err error) {
    189 	// See the comments in Write() for the details of this format.
    190 	if h.used == 0 {
    191 		return
    192 	}
    193 	h.buf[0] = ' '
    194 	h.buf[1] = ' '
    195 	h.buf[2] = ' '
    196 	h.buf[3] = ' '
    197 	h.buf[4] = '|'
    198 	nBytes := h.used
    199 	for h.used < 16 {
    200 		l := 3
    201 		if h.used == 7 {
    202 			l = 4
    203 		} else if h.used == 15 {
    204 			l = 5
    205 		}
    206 		_, err = h.w.Write(h.buf[:l])
    207 		if err != nil {
    208 			return
    209 		}
    210 		h.used++
    211 	}
    212 	h.rightChars[nBytes] = '|'
    213 	h.rightChars[nBytes+1] = '\n'
    214 	_, err = h.w.Write(h.rightChars[:nBytes+2])
    215 	return
    216 }
    217