All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.internal.ByteBufferDataOutput Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2018 SnappyData, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */

package com.gemstone.gemfire.internal;

import java.io.Closeable;
import java.io.DataOutput;
import java.io.IOException;
import java.io.UTFDataFormatException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import javax.annotation.Nonnull;

import com.gemstone.gemfire.DataSerializer;
import com.gemstone.gemfire.internal.cache.DiskEntry;
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
import com.gemstone.gemfire.internal.cache.store.SerializedDiskBuffer;
import com.gemstone.gemfire.internal.shared.BufferAllocator;
import com.gemstone.gemfire.internal.shared.ClientSharedUtils;
import com.gemstone.gemfire.internal.shared.OutputStreamChannel;
import com.gemstone.gemfire.internal.shared.Version;
import com.gemstone.gemfire.internal.shared.unsafe.ChannelBufferUnsafeDataOutputStream;

/**
 * A {@link SerializedDiskBuffer} that implements {@link DataOutput} writing
 * to a ByteBuffer expanding it as required.
 * 

* Note: this can be further optimized by using the Unsafe API rather than * going through ByteBuffer API (e.g. see ChannelBufferUnsafeDataOutputStream) * but won't have an effect for large byte array writes (like for column data) */ public final class ByteBufferDataOutput extends SerializedDiskBuffer implements DataOutput, Closeable, VersionedDataStream { private static final int INITIAL_SIZE = 1024; private final BufferAllocator allocator; private ByteBuffer buffer; private final Version version; private String bufferOwner = "DATAOUTPUT"; public ByteBufferDataOutput(Version version) { this(INITIAL_SIZE, version); } /** * This constructor can be used to give non-default buffer owner. * Kind of hack, to avoid large scale refactoring to pass shoulEvict * flag to UMM. */ public ByteBufferDataOutput(int initialSize, BufferAllocator allocator, Version version, String bufferOwner) { this.allocator = allocator; this.buffer = allocator.allocate(initialSize, bufferOwner) .order(ByteOrder.BIG_ENDIAN); this.version = version; this.bufferOwner = bufferOwner; } public ByteBufferDataOutput(int initialSize, Version version) { // this uses allocations and expansion via BufferAllocator that has both // better efficiency for direct buffer (in expand) and applies system limits this.allocator = GemFireCacheImpl.getCurrentBufferAllocator(); this.buffer = allocator.allocate(initialSize, bufferOwner) .order(ByteOrder.BIG_ENDIAN); this.version = version; } /** * Initialize the buffer serializing the given object to a byte buffer * (with initial reference count as 1). Normally there should be only * one calling thread that will {@link #release()} this when done. */ public ByteBufferDataOutput serialize(Object obj) throws IOException { this.buffer.rewind(); DataSerializer.writeObject(obj, this); this.buffer.flip(); return this; } @Override public ByteBuffer getBufferRetain() { return retain() ? this.buffer.duplicate() : DiskEntry.Helper.NULL_BUFFER.duplicate(); } @Override public ByteBuffer getBuffer() { return this.buffer.duplicate(); } @Override public boolean needsRelease() { final ByteBuffer buffer = this.buffer; return buffer != null && buffer.isDirect(); } @Override protected synchronized void releaseBuffer() { final ByteBuffer buffer = this.buffer; if (buffer != null) { this.allocator.release(buffer); this.buffer = null; } } @Override public synchronized void write( OutputStreamChannel channel) throws IOException { final ByteBuffer buffer = this.buffer.duplicate(); if (buffer != null) { write(channel, buffer); } else { channel.write(DSCODE.NULL); } } @Override public int size() { // deliberately not synchronized since size() should always be protected // with a retain call if required and not lead to indeterminate results // due to an unexpected intervening release final ByteBuffer buffer = this.buffer; if (buffer != null) { return buffer.limit(); } else { return 0; } } @Override public int getOffHeapSizeInBytes() { return 0; // will not be stored in region } @Override public Version getVersion() { return this.version; } @Override public void write(int b) throws IOException { ensureCapacity(1); this.buffer.put((byte)b); } @Override public void write(@Nonnull byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(@Nonnull byte[] b, int off, int len) throws IOException { ensureCapacity(len); this.buffer.put(b, off, len); } @Override public void writeBoolean(boolean v) throws IOException { ensureCapacity(1); this.buffer.put(v ? (byte)1 : 0); } @Override public void writeByte(int v) throws IOException { ensureCapacity(1); this.buffer.put((byte)v); } @Override public void writeShort(int v) throws IOException { ensureCapacity(2); this.buffer.putShort((short)v); } @Override public void writeChar(int v) throws IOException { writeShort(v); } @Override public void writeInt(int v) throws IOException { ensureCapacity(4); this.buffer.putInt(v); } @Override public void writeLong(long v) throws IOException { ensureCapacity(8); this.buffer.putLong(v); } @Override public void writeFloat(float v) throws IOException { writeInt(Float.floatToIntBits(v)); } @Override public void writeDouble(double v) throws IOException { writeLong(Double.doubleToLongBits(v)); } @Override public void writeUTF(@Nonnull String s) throws IOException { // first calculate the UTF encoded length final int strLen = s.length(); final int utfLen = ClientSharedUtils.getUTFLength(s, strLen); if (utfLen > 65535) { throw new UTFDataFormatException("encoded string too long: " + utfLen + " bytes"); } // make required space ensureCapacity(utfLen + 2); final ByteBuffer buffer = this.buffer; // write the length first buffer.putShort((short)utfLen); // now write as UTF data using unsafe API final Object baseObject = this.allocator.baseObject(buffer); final long address = this.allocator.baseOffset(buffer); final int position = buffer.position(); ChannelBufferUnsafeDataOutputStream.writeUTFSegmentNoOverflow(s, 0, strLen, utfLen, baseObject, address + position); buffer.position(position + utfLen); } @Override public void writeBytes(@Nonnull String s) throws IOException { if (s.length() > 0) { final byte[] bytes = s.getBytes(StandardCharsets.US_ASCII); ensureCapacity(bytes.length); this.buffer.put(bytes); } } @Override public void writeChars(@Nonnull String s) throws IOException { int len = s.length(); if (len > 0) { final int required = len << 1; if (required < 0) { throw new IOException("Buffer overflow with required=" + required); } ensureCapacity(required); for (int i = 0; i < len; i++) { this.buffer.putChar(s.charAt(i)); } } } @Override public void close() throws IOException { } @Override public String toString() { return ClientSharedUtils.toString(this.buffer); } protected void ensureCapacity(int required) { final ByteBuffer buffer = this.buffer; final int position = buffer.position(); if (buffer.limit() - position >= required) return; buffer.flip(); ByteBuffer newBuffer = this.allocator.expand(buffer, required, bufferOwner); // set the position of newBuffer newBuffer.position(position); this.buffer = newBuffer; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy