1e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// Protocol Buffers - Google's data interchange format
2e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// Copyright 2008 Google Inc.  All rights reserved.
3e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// http://code.google.com/p/protobuf/
4e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville//
5e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// Redistribution and use in source and binary forms, with or without
6e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// modification, are permitted provided that the following conditions are
7e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// met:
8e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville//
9e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville//     * Redistributions of source code must retain the above copyright
10e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// notice, this list of conditions and the following disclaimer.
11e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville//     * Redistributions in binary form must reproduce the above
12e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// copyright notice, this list of conditions and the following disclaimer
13e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// in the documentation and/or other materials provided with the
14e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// distribution.
15e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville//     * Neither the name of Google Inc. nor the names of its
16e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// contributors may be used to endorse or promote products derived from
17e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// this software without specific prior written permission.
18e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville//
19e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
31e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Savillepackage com.google.protobuf.micro;
32e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
33e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Savilleimport java.io.UnsupportedEncodingException;
34e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
35e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville/**
36e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville * Immutable array of bytes.
37e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville *
38e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville * @author crazybob@google.com Bob Lee
39e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville * @author kenton@google.com Kenton Varda
40e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville */
41e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Savillepublic final class ByteStringMicro {
42e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  private final byte[] bytes;
43e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
44e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  private ByteStringMicro(final byte[] bytes) {
45e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    this.bytes = bytes;
46e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
47e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
48e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
49e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Gets the byte at the given index.
50e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   *
51e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
52e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
53e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public byte byteAt(final int index) {
54e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    return bytes[index];
55e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
56e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
57e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
58e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Gets the number of bytes.
59e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
60e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public int size() {
61e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    return bytes.length;
62e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
63e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
64e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
65e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Returns {@code true} if the size is {@code 0}, {@code false} otherwise.
66e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
67e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public boolean isEmpty() {
68e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    return bytes.length == 0;
69e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
70e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
71e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  // =================================================================
72e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  // byte[] -> ByteStringMicro
73e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
74e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
75e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Empty ByteStringMicro.
76e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
77e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public static final ByteStringMicro EMPTY = new ByteStringMicro(new byte[0]);
78e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
79e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
80e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Copies the given bytes into a {@code ByteStringMicro}.
81e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
82e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public static ByteStringMicro copyFrom(final byte[] bytes, final int offset,
83e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville                                    final int size) {
84e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    final byte[] copy = new byte[size];
85e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    System.arraycopy(bytes, offset, copy, 0, size);
86e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    return new ByteStringMicro(copy);
87e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
88e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
89e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
90e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Copies the given bytes into a {@code ByteStringMicro}.
91e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
92e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public static ByteStringMicro copyFrom(final byte[] bytes) {
93e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    return copyFrom(bytes, 0, bytes.length);
94e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
95e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
96e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
97e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Encodes {@code text} into a sequence of bytes using the named charset
98e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * and returns the result as a {@code ByteStringMicro}.
99e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
100e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public static ByteStringMicro copyFrom(final String text, final String charsetName)
101e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      throws UnsupportedEncodingException {
102e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    return new ByteStringMicro(text.getBytes(charsetName));
103e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
104e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
105e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
106e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Encodes {@code text} into a sequence of UTF-8 bytes and returns the
107e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * result as a {@code ByteStringMicro}.
108e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
109e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public static ByteStringMicro copyFromUtf8(final String text) {
110e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    try {
111e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      return new ByteStringMicro(text.getBytes("UTF-8"));
112e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    } catch (UnsupportedEncodingException e) {
113e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      throw new RuntimeException("UTF-8 not supported?");
114e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    }
115e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
116e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
117e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  // =================================================================
118e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  // ByteStringMicro -> byte[]
119e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
120e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
121e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Copies bytes into a buffer at the given offset.
122e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   *
123e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * @param target buffer to copy into
124e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * @param offset in the target buffer
125e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
126e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public void copyTo(final byte[] target, final int offset) {
127e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    System.arraycopy(bytes, 0, target, offset, bytes.length);
128e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
129e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
130e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
131e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Copies bytes into a buffer.
132e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   *
133e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * @param target buffer to copy into
134e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * @param sourceOffset offset within these bytes
135e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * @param targetOffset offset within the target buffer
136e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * @param size number of bytes to copy
137e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
138e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public void copyTo(final byte[] target, final int sourceOffset,
139e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville                     final int targetOffset,
140e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      final int size) {
141e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    System.arraycopy(bytes, sourceOffset, target, targetOffset, size);
142e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
143e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
144e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
145e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Copies bytes to a {@code byte[]}.
146e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
147e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public byte[] toByteArray() {
148e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    final int size = bytes.length;
149e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    final byte[] copy = new byte[size];
150e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    System.arraycopy(bytes, 0, copy, 0, size);
151e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    return copy;
152e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
153e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
154e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
155e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Constructs a new {@code String} by decoding the bytes using the
156e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * specified charset.
157e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
158e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public String toString(final String charsetName)
159e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      throws UnsupportedEncodingException {
160e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    return new String(bytes, charsetName);
161e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
162e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
163e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  /**
164e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   * Constructs a new {@code String} by decoding the bytes as UTF-8.
165e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville   */
166e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public String toStringUtf8() {
167e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    try {
168e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      return new String(bytes, "UTF-8");
169e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    } catch (UnsupportedEncodingException e) {
170e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      throw new RuntimeException("UTF-8 not supported?");
171e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    }
172e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
173e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
174e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  // =================================================================
175e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  // equals() and hashCode()
176e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
177e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  //@Override for compatibility with Java 1.3 code we can't use annotations
178e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public boolean equals(final Object o) {
179e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    if (o == this) {
180e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      return true;
181e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    }
182e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
183e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    if (!(o instanceof ByteStringMicro)) {
184e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      return false;
185e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    }
186e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
187e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    final ByteStringMicro other = (ByteStringMicro) o;
188e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    final int size = bytes.length;
189e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    if (size != other.bytes.length) {
190e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      return false;
191e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    }
192e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
193e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    final byte[] thisBytes = bytes;
194e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    final byte[] otherBytes = other.bytes;
195e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    for (int i = 0; i < size; i++) {
196e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      if (thisBytes[i] != otherBytes[i]) {
197e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville        return false;
198e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      }
199e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    }
200e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
201e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    return true;
202e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
203e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
204e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  private volatile int hash = 0;
205e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
206e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  //@Override for compatibility with Java 1.3 code we can't use annotations
207e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  public int hashCode() {
208e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    int h = hash;
209e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
210e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    if (h == 0) {
211e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      final byte[] thisBytes = bytes;
212e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      final int size = bytes.length;
213e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
214e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      h = size;
215e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      for (int i = 0; i < size; i++) {
216e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville        h = h * 31 + thisBytes[i];
217e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      }
218e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      if (h == 0) {
219e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville        h = 1;
220e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      }
221e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
222e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville      hash = h;
223e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    }
224e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville
225e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville    return h;
226e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville  }
227e2d542951c059563a3b7f74c257dac4f222d9dc5Wink Saville}
228