github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/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  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  }