| // Copyright 2009 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | // Package hex implements hexadecimal encoding and decoding. | 
 | package hex | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"errors" | 
 | 	"fmt" | 
 | 	"io" | 
 | ) | 
 |  | 
 | var hextable = [16]byte{ | 
 | 	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', | 
 | 	'a', 'b', 'c', 'd', 'e', 'f', | 
 | } | 
 |  | 
 | // EncodedLen returns the length of an encoding of n source bytes. | 
 | // Specifically, it returns n * 2. | 
 | func EncodedLen(n int) int { return n * 2 } | 
 |  | 
 | // Encode encodes src into EncodedLen(len(src)) | 
 | // bytes of dst. As a convenience, it returns the number | 
 | // of bytes written to dst, but this value is always EncodedLen(len(src)). | 
 | // Encode implements hexadecimal encoding. | 
 | func Encode(dst, src []byte) int { | 
 | 	for i, v := range src { | 
 | 		dst[i*2] = hextable[v>>4] | 
 | 		dst[i*2+1] = hextable[v&0x0f] | 
 | 	} | 
 |  | 
 | 	return len(src) * 2 | 
 | } | 
 |  | 
 | // ErrLength results from decoding an odd length slice. | 
 | var ErrLength = errors.New("encoding/hex: odd length hex string") | 
 |  | 
 | // InvalidByteError values describe errors resulting from an invalid byte in a hex string. | 
 | type InvalidByteError byte | 
 |  | 
 | func (e InvalidByteError) Error() string { | 
 | 	return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e)) | 
 | } | 
 |  | 
 | // DecodedLen returns the length of a decoding of x source bytes. | 
 | // Specifically, it returns x / 2. | 
 | func DecodedLen(x int) int { return x / 2 } | 
 |  | 
 | // Decode decodes src into DecodedLen(len(src)) bytes, | 
 | // returning the actual number of bytes written to dst. | 
 | // | 
 | // Decode expects that src contain only hexadecimal | 
 | // characters and that src should have an even length. | 
 | func Decode(dst, src []byte) (int, error) { | 
 | 	if len(src)%2 == 1 { | 
 | 		return 0, ErrLength | 
 | 	} | 
 |  | 
 | 	for i := 0; i < len(src)/2; i++ { | 
 | 		a, ok := fromHexChar(src[i*2]) | 
 | 		if !ok { | 
 | 			return 0, InvalidByteError(src[i*2]) | 
 | 		} | 
 | 		b, ok := fromHexChar(src[i*2+1]) | 
 | 		if !ok { | 
 | 			return 0, InvalidByteError(src[i*2+1]) | 
 | 		} | 
 | 		dst[i] = (a << 4) | b | 
 | 	} | 
 |  | 
 | 	return len(src) / 2, nil | 
 | } | 
 |  | 
 | // fromHexChar converts a hex character into its value and a success flag. | 
 | func fromHexChar(c byte) (byte, bool) { | 
 | 	switch { | 
 | 	case '0' <= c && c <= '9': | 
 | 		return c - '0', true | 
 | 	case 'a' <= c && c <= 'f': | 
 | 		return c - 'a' + 10, true | 
 | 	case 'A' <= c && c <= 'F': | 
 | 		return c - 'A' + 10, true | 
 | 	} | 
 |  | 
 | 	return 0, false | 
 | } | 
 |  | 
 | // EncodeToString returns the hexadecimal encoding of src. | 
 | func EncodeToString(src []byte) string { | 
 | 	dst := make([]byte, EncodedLen(len(src))) | 
 | 	Encode(dst, src) | 
 | 	return string(dst) | 
 | } | 
 |  | 
 | // DecodeString returns the bytes represented by the hexadecimal string s. | 
 | func DecodeString(s string) ([]byte, error) { | 
 | 	src := []byte(s) | 
 | 	dst := make([]byte, DecodedLen(len(src))) | 
 | 	_, err := Decode(dst, src) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	return dst, nil | 
 | } | 
 |  | 
 | // Dump returns a string that contains a hex dump of the given data. The format | 
 | // of the hex dump matches the output of `hexdump -C` on the command line. | 
 | func Dump(data []byte) string { | 
 | 	var buf bytes.Buffer | 
 | 	dumper := Dumper(&buf) | 
 | 	dumper.Write(data) | 
 | 	dumper.Close() | 
 | 	return buf.String() | 
 | } | 
 |  | 
 | // Dumper returns a WriteCloser that writes a hex dump of all written data to | 
 | // w. The format of the dump matches the output of `hexdump -C` on the command | 
 | // line. | 
 | func Dumper(w io.Writer) io.WriteCloser { | 
 | 	return &dumper{w: w} | 
 | } | 
 |  | 
 | type dumper struct { | 
 | 	w          io.Writer | 
 | 	rightChars [18]byte | 
 | 	buf        [14]byte | 
 | 	used       int  // number of bytes in the current line | 
 | 	n          uint // number of bytes, total | 
 | } | 
 |  | 
 | func toChar(b byte) byte { | 
 | 	if b < 32 || b > 126 { | 
 | 		return '.' | 
 | 	} | 
 | 	return b | 
 | } | 
 |  | 
 | func (h *dumper) Write(data []byte) (n int, err error) { | 
 | 	// Output lines look like: | 
 | 	// 00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=| | 
 | 	// ^ offset                          ^ extra space              ^ ASCII of line. | 
 | 	for i := range data { | 
 | 		if h.used == 0 { | 
 | 			// At the beginning of a line we print the current | 
 | 			// offset in hex. | 
 | 			h.buf[0] = byte(h.n >> 24) | 
 | 			h.buf[1] = byte(h.n >> 16) | 
 | 			h.buf[2] = byte(h.n >> 8) | 
 | 			h.buf[3] = byte(h.n) | 
 | 			Encode(h.buf[4:], h.buf[:4]) | 
 | 			h.buf[12] = ' ' | 
 | 			h.buf[13] = ' ' | 
 | 			_, err = h.w.Write(h.buf[4:]) | 
 | 			if err != nil { | 
 | 				return | 
 | 			} | 
 | 		} | 
 | 		Encode(h.buf[:], data[i:i+1]) | 
 | 		h.buf[2] = ' ' | 
 | 		l := 3 | 
 | 		if h.used == 7 { | 
 | 			// There's an additional space after the 8th byte. | 
 | 			h.buf[3] = ' ' | 
 | 			l = 4 | 
 | 		} else if h.used == 15 { | 
 | 			// At the end of the line there's an extra space and | 
 | 			// the bar for the right column. | 
 | 			h.buf[3] = ' ' | 
 | 			h.buf[4] = '|' | 
 | 			l = 5 | 
 | 		} | 
 | 		_, err = h.w.Write(h.buf[:l]) | 
 | 		if err != nil { | 
 | 			return | 
 | 		} | 
 | 		n++ | 
 | 		h.rightChars[h.used] = toChar(data[i]) | 
 | 		h.used++ | 
 | 		h.n++ | 
 | 		if h.used == 16 { | 
 | 			h.rightChars[16] = '|' | 
 | 			h.rightChars[17] = '\n' | 
 | 			_, err = h.w.Write(h.rightChars[:]) | 
 | 			if err != nil { | 
 | 				return | 
 | 			} | 
 | 			h.used = 0 | 
 | 		} | 
 | 	} | 
 | 	return | 
 | } | 
 |  | 
 | func (h *dumper) Close() (err error) { | 
 | 	// See the comments in Write() for the details of this format. | 
 | 	if h.used == 0 { | 
 | 		return | 
 | 	} | 
 | 	h.buf[0] = ' ' | 
 | 	h.buf[1] = ' ' | 
 | 	h.buf[2] = ' ' | 
 | 	h.buf[3] = ' ' | 
 | 	h.buf[4] = '|' | 
 | 	nBytes := h.used | 
 | 	for h.used < 16 { | 
 | 		l := 3 | 
 | 		if h.used == 7 { | 
 | 			l = 4 | 
 | 		} else if h.used == 15 { | 
 | 			l = 5 | 
 | 		} | 
 | 		_, err = h.w.Write(h.buf[:l]) | 
 | 		if err != nil { | 
 | 			return | 
 | 		} | 
 | 		h.used++ | 
 | 	} | 
 | 	h.rightChars[nBytes] = '|' | 
 | 	h.rightChars[nBytes+1] = '\n' | 
 | 	_, err = h.w.Write(h.rightChars[:nBytes+2]) | 
 | 	return | 
 | } |