src/pkg/encoding/hex/hex.go - The Go Programming Language

Golang

Source file src/pkg/encoding/hex/hex.go

     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			}
   150			Encode(h.buf[:], data[i:i+1])
   151			h.buf[2] = ' '
   152			l := 3
   153			if h.used == 7 {
   154				// There's an additional space after the 8th byte.
   155				h.buf[3] = ' '
   156				l = 4
   157			} else if h.used == 15 {
   158				// At the end of the line there's an extra space and
   159				// the bar for the right column.
   160				h.buf[3] = ' '
   161				h.buf[4] = '|'
   162				l = 5
   163			}
   164			_, err = h.w.Write(h.buf[:l])
   165			if err != nil {
   166				return
   167			}
   168			n++
   169			h.rightChars[h.used] = toChar(data[i])
   170			h.used++
   171			h.n++
   172			if h.used == 16 {
   173				h.rightChars[16] = '|'
   174				h.rightChars[17] = '\n'
   175				_, err = h.w.Write(h.rightChars[:])
   176				if err != nil {
   177					return
   178				}
   179				h.used = 0
   180			}
   181		}
   182		return
   183	}
   184	
   185	func (h *dumper) Close() (err error) {
   186		// See the comments in Write() for the details of this format.
   187		if h.used == 0 {
   188			return
   189		}
   190		h.buf[0] = ' '
   191		h.buf[1] = ' '
   192		h.buf[2] = ' '
   193		h.buf[3] = ' '
   194		h.buf[4] = '|'
   195		nBytes := h.used
   196		for h.used < 16 {
   197			l := 3
   198			if h.used == 7 {
   199				l = 4
   200			} else if h.used == 15 {
   201				l = 5
   202			}
   203			_, err = h.w.Write(h.buf[:l])
   204			if err != nil {
   205				return
   206			}
   207			h.used++
   208		}
   209		h.rightChars[nBytes] = '|'
   210		h.rightChars[nBytes+1] = '\n'
   211		_, err = h.w.Write(h.rightChars[:nBytes+2])
   212		return
   213	}