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