The Go Programming Language

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		"io"
    11		"os"
    12		"strconv"
    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	// OddLengthInputError results from decoding an odd length slice.
    34	type OddLengthInputError struct{}
    35	
    36	func (OddLengthInputError) String() string { return "odd length hex string" }
    37	
    38	// InvalidHexCharError results from finding an invalid character in a hex string.
    39	type InvalidHexCharError byte
    40	
    41	func (e InvalidHexCharError) String() string {
    42		return "invalid hex char: " + strconv.Itoa(int(e))
    43	}
    44	
    45	func DecodedLen(x int) int { return x / 2 }
    46	
    47	// Decode decodes src into DecodedLen(len(src)) bytes, returning the actual
    48	// number of bytes written to dst.
    49	//
    50	// If Decode encounters invalid input, it returns an OddLengthInputError or an
    51	// InvalidHexCharError.
    52	func Decode(dst, src []byte) (int, os.Error) {
    53		if len(src)%2 == 1 {
    54			return 0, OddLengthInputError{}
    55		}
    56	
    57		for i := 0; i < len(src)/2; i++ {
    58			a, ok := fromHexChar(src[i*2])
    59			if !ok {
    60				return 0, InvalidHexCharError(src[i*2])
    61			}
    62			b, ok := fromHexChar(src[i*2+1])
    63			if !ok {
    64				return 0, InvalidHexCharError(src[i*2+1])
    65			}
    66			dst[i] = (a << 4) | b
    67		}
    68	
    69		return len(src) / 2, nil
    70	}
    71	
    72	// fromHexChar converts a hex character into its value and a success flag.
    73	func fromHexChar(c byte) (byte, bool) {
    74		switch {
    75		case '0' <= c && c <= '9':
    76			return c - '0', true
    77		case 'a' <= c && c <= 'f':
    78			return c - 'a' + 10, true
    79		case 'A' <= c && c <= 'F':
    80			return c - 'A' + 10, true
    81		}
    82	
    83		return 0, false
    84	}
    85	
    86	// EncodeToString returns the hexadecimal encoding of src.
    87	func EncodeToString(src []byte) string {
    88		dst := make([]byte, EncodedLen(len(src)))
    89		Encode(dst, src)
    90		return string(dst)
    91	}
    92	
    93	// DecodeString returns the bytes represented by the hexadecimal string s.
    94	func DecodeString(s string) ([]byte, os.Error) {
    95		src := []byte(s)
    96		dst := make([]byte, DecodedLen(len(src)))
    97		_, err := Decode(dst, src)
    98		if err != nil {
    99			return nil, err
   100		}
   101		return dst, nil
   102	}
   103	
   104	// Dump returns a string that contains a hex dump of the given data. The format
   105	// of the hex dump matches the output of `hexdump -C` on the command line.
   106	func Dump(data []byte) string {
   107		buf := bytes.NewBuffer(nil)
   108		dumper := Dumper(buf)
   109		dumper.Write(data)
   110		dumper.Close()
   111		return string(buf.Bytes())
   112	}
   113	
   114	// Dumper returns a WriteCloser that writes a hex dump of all written data to
   115	// w. The format of the dump matches the output of `hexdump -C` on the command
   116	// line.
   117	func Dumper(w io.Writer) io.WriteCloser {
   118		return &dumper{w: w}
   119	}
   120	
   121	type dumper struct {
   122		w          io.Writer
   123		rightChars [18]byte
   124		buf        [14]byte
   125		used       int  // number of bytes in the current line
   126		n          uint // number of bytes, total
   127	}
   128	
   129	func toChar(b byte) byte {
   130		if b < 32 || b > 126 {
   131			return '.'
   132		}
   133		return b
   134	}
   135	
   136	func (h *dumper) Write(data []byte) (n int, err os.Error) {
   137		// Output lines look like:
   138		// 00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=|
   139		// ^ offset                          ^ extra space              ^ ASCII of line.
   140		for i := range data {
   141			if h.used == 0 {
   142				// At the beginning of a line we print the current
   143				// offset in hex.
   144				h.buf[0] = byte(h.n >> 24)
   145				h.buf[1] = byte(h.n >> 16)
   146				h.buf[2] = byte(h.n >> 8)
   147				h.buf[3] = byte(h.n)
   148				Encode(h.buf[4:], h.buf[:4])
   149				h.buf[12] = ' '
   150				h.buf[13] = ' '
   151				_, err = h.w.Write(h.buf[4:])
   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 os.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	}

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.