001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.util;
019
020import java.io.ByteArrayInputStream;
021import java.io.ByteArrayOutputStream;
022import java.io.DataInputStream;
023import java.io.DataOutputStream;
024import java.io.IOException;
025import java.util.ArrayList;
026import java.util.List;
027import org.apache.hadoop.io.DataInputBuffer;
028import org.apache.hadoop.io.Writable;
029import org.apache.yetus.audience.InterfaceAudience;
030
031/**
032 * Utility class with methods for manipulating Writable objects
033 */
034@InterfaceAudience.Private
035public class Writables {
036  /**
037   * Get the Writable's contents as a byte array
038   * @param w writable
039   * @return The bytes of <code>w</code> gotten by running its
040   *         {@link Writable#write(java.io.DataOutput)} method.
041   * @throws IOException e
042   * @see #getWritable(byte[], Writable)
043   */
044  public static byte[] getBytes(final Writable w) throws IOException {
045    if (w == null) {
046      throw new IllegalArgumentException("Writable cannot be null");
047    }
048    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
049    DataOutputStream out = new DataOutputStream(byteStream);
050    try {
051      w.write(out);
052      out.close();
053      out = null;
054      return byteStream.toByteArray();
055    } finally {
056      if (out != null) {
057        out.close();
058      }
059    }
060  }
061
062  /**
063   * Put a bunch of Writables as bytes all into the one byte array.
064   * @param ws writable
065   * @return The bytes of <code>w</code> gotten by running its
066   *         {@link Writable#write(java.io.DataOutput)} method.
067   * @throws IOException e
068   */
069  public static byte[] getBytes(final Writable... ws) throws IOException {
070    List<byte[]> bytes = new ArrayList<>(ws.length);
071    int size = 0;
072    for (Writable w : ws) {
073      byte[] b = getBytes(w);
074      size += b.length;
075      bytes.add(b);
076    }
077    byte[] result = new byte[size];
078    int offset = 0;
079    for (byte[] b : bytes) {
080      System.arraycopy(b, 0, result, offset, b.length);
081      offset += b.length;
082    }
083    return result;
084  }
085
086  /**
087   * Set bytes into the passed Writable by calling its
088   * {@link Writable#readFields(java.io.DataInput)}.
089   * @param bytes serialized bytes
090   * @param w     An empty Writable (usually made by calling the null-arg constructor).
091   * @return The passed Writable after its readFields has been called fed by the passed
092   *         <code>bytes</code> array or IllegalArgumentException if passed null or an empty
093   *         <code>bytes</code> array.
094   * @throws IOException e
095   */
096  public static Writable getWritable(final byte[] bytes, final Writable w) throws IOException {
097    return getWritable(bytes, 0, bytes.length, w);
098  }
099
100  /**
101   * Set bytes into the passed Writable by calling its
102   * {@link Writable#readFields(java.io.DataInput)}.
103   * @param bytes  serialized bytes
104   * @param offset offset into array
105   * @param length length of data
106   * @param w      An empty Writable (usually made by calling the null-arg constructor).
107   * @return The passed Writable after its readFields has been called fed by the passed
108   *         <code>bytes</code> array or IllegalArgumentException if passed null or an empty
109   *         <code>bytes</code> array.
110   * @throws IOException e
111   */
112  public static Writable getWritable(final byte[] bytes, final int offset, final int length,
113    final Writable w) throws IOException {
114    if (bytes == null || length <= 0) {
115      throw new IllegalArgumentException("Can't build a writable with empty " + "bytes array");
116    }
117    if (w == null) {
118      throw new IllegalArgumentException("Writable cannot be null");
119    }
120    DataInputBuffer in = new DataInputBuffer();
121    try {
122      in.reset(bytes, offset, length);
123      w.readFields(in);
124      return w;
125    } finally {
126      in.close();
127    }
128  }
129
130  /**
131   * Copy one Writable to another. Copies bytes using data streams.
132   * @param src Source Writable
133   * @param tgt Target Writable
134   * @return The target Writable.
135   * @throws IOException e
136   */
137  public static Writable copyWritable(final Writable src, final Writable tgt) throws IOException {
138    return copyWritable(getBytes(src), tgt);
139  }
140
141  /**
142   * Copy one Writable to another. Copies bytes using data streams.
143   * @param bytes Source Writable
144   * @param tgt   Target Writable
145   * @return The target Writable.
146   * @throws IOException e
147   */
148  public static Writable copyWritable(final byte[] bytes, final Writable tgt) throws IOException {
149    DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
150    try {
151      tgt.readFields(dis);
152    } finally {
153      dis.close();
154    }
155    return tgt;
156  }
157}