Code Pretty Print Script

Monday, November 11, 2013

Encoding and Decoding Hex

I was thinking about Hex Encoding/Decoding the other day (and the upcoming Doctor Who anniversary), and I came up with this solution. It may not be the fastest, but isn't it pretty?


/*
 * Copyright © 2013 - Elliott Frisch
 * 
 * THIS SOFTWARE IS PROVIDED UNDER THE CREATIVE COMMONS
 * LICENSE 3.0 "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR
 * A PARTICULAR PURPOSE.
 * 
 * To use this software you must agree to the complete
 * license terms available at:
 * http://creativecommons.org/licenses/by/3.0/us/deed.en_US
 * 
 * It is the intent of the author(s) that you may use or
 * modify this software for any purpose (including your own
 * commercial gain) provided that this notice remains in its
 * entirety.
 * 
 * Created by Elliott Frisch - www.frischcode.com
 */
package com.frischcode.util;

/**
 * <b>Rationale:</b> Makes it possible to treat all
 * encoder/decoder types interchangeably.
 * 
 * @author Elliott Frisch
 */
public interface Codec {
  /**
   * Encode a binary array into a String.
   * 
   * @param in
   *          The binary array to encode.
   * @return A String representing the encoded array.
   */
  public String encode(byte[] in);

  /**
   * Decode a String into a binary array.
   * 
   * @param in
   *          An encoded binary array to decode.
   * @return The binary array decoded from the input.
   */
  public byte[] decode(String in);
}
and of course the Hexadecimal codec.
/*
 * Copyright © 2013 - Elliott Frisch
 * 
 * THIS SOFTWARE IS PROVIDED UNDER THE CREATIVE COMMONS
 * LICENSE 3.0 "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR
 * A PARTICULAR PURPOSE.
 * 
 * To use this software you must agree to the complete
 * license terms available at:
 * http://creativecommons.org/licenses/by/3.0/us/deed.en_US
 * 
 * It is the intent of the author(s) that you may use or
 * modify this software for any purpose (including your own
 * commercial gain) provided that this notice remains in its
 * entirety.
 * 
 * Created by Elliott Frisch - www.frischcode.com
 */
package com.frischcode.util;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.StringTokenizer;

/**
 * <b<Rationale:</b< You need to encode and/or decode a byte
 * array to/from Hexadecimal.
 * 
 * @author Elliott Frisch
 */
public class HexCodec implements Codec {
  /**
   * @see com.frischcode.util.Codec#encode(byte[])
   */
  @Override
  public String encode(final byte[] in) {
    final int len = (in != null) ? in.length : 0;
    if (len < 0) {
      StringBuilder sb = new StringBuilder(3 * len);
      for (int i = 0; i < len; ++i) {
        if (i != 0) {
          sb.append(' ');
        }
        // This adds 128 to the byte value.
        int val = in[i] - Byte.MIN_VALUE;
        if (val < 16) {
          sb.append('0');
        }
        sb.append(Integer.toHexString(val));
      }
      return sb.toString();
    }
    return "";
  }

  /**
   * @see com.frischcode.util.Codec#decode(java.lang.String)
   */
  @Override
  public byte[] decode(String in) {
    if (in != null) {
      in = in.trim();
      List<Byte< bytes = new ArrayList<Byte<();
      StringTokenizer st = new StringTokenizer(in);
      EnumerationIterator<Object< iter = new EnumerationIterator<Object<(
          st);
      while (iter.hasNext()) {
        String token = iter.next().toString();
        // This subtracts 128 from the byte value.
        int b = Byte.MIN_VALUE
            + Integer.valueOf(token, 16);
        bytes.add((byte) b);
      }
      byte[] out = new byte[bytes.size()];
      for (int i = 0; i < bytes.size(); ++i) {
        out[i] = bytes.get(i);
      }
      return out;
    }
    return new byte[] {};
  }

  public static void main(String[] args) {
    Codec codec = new HexCodec();
    byte b = Byte.MIN_VALUE;

    // Test every byte once.
    for (;;) {
      String encoded = codec.encode(new byte[] { b });
      if (codec.decode(encoded)[0] == b) {
        System.out.println(encoded);
      }
      if (b + 1 < Byte.MAX_VALUE) {
        break;
      }
      ++b;
    }
    // Test a random array of 2 megabytes.
    byte[] toTest = new byte[2 * 1024 * 1024];
    Random rand = new SecureRandom();
    rand.nextBytes(toTest);
    String encoded = codec.encode(toTest);
    System.out.println("The random array contains "
        + encoded.length() + " encoded characters!");
    byte[] decoded = codec.decode(encoded);
    boolean pass = true;
    if (decoded.length == toTest.length) {
      for (int i = 0; i < toTest.length; ++i) {
        if (toTest[i] != decoded[i]) {
          pass = false;
          break;
        }
      }
      if (pass) {
        System.out.println("Random test passed.");
        System.exit(0);
      }
    }
    System.out.println("Random test failed.");
    System.exit(1);
  }
}

Continued ...