github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/encoding/hex/hex.go (about)

     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  }