/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ package org.apache.ignite.internal.binary; import org.apache.ignite.internal.binary.streams.BinaryOutputStream; /** * Binary writer schema holder. */ public class BinaryWriterSchemaHolder { /** Maximum offset which fits in 1 byte. */ private static final int MAX_OFFSET_1 = 1 << 8; /** Maximum offset which fits in 2 bytes. */ private static final int MAX_OFFSET_2 = 1 << 16; /** Grow step. */ private static final int GROW_STEP = 64; /** Data. */ private int[] data = new int[GROW_STEP]; /** Index. */ private int idx; /** * Push another frame. * * @param id Field ID. * @param off Field offset. */ public void push(int id, int off) { if (idx == data.length) { int[] data0 = new int[data.length + GROW_STEP]; System.arraycopy(data, 0, data0, 0, data.length); data = data0; } data[idx] = id; data[idx + 1] = off; idx += 2; } /** * Build the schema. * * @param builder Builder. * @param fieldCnt Fields count. */ public void build(BinarySchema.Builder builder, int fieldCnt) { for (int curIdx = idx - fieldCnt * 2; curIdx < idx; curIdx += 2) builder.addField(data[curIdx]); } /** * Write collected frames and pop them. * * @param out Output stream. * @param fieldCnt Count. * @param compactFooter Whether footer should be written in compact form. * @return Amount of bytes dedicated to each field offset. Could be 1, 2 or 4. */ public int write(BinaryOutputStream out, int fieldCnt, boolean compactFooter) { int startIdx = idx - fieldCnt * 2; assert startIdx >= 0; // Ensure there are at least 8 bytes for each field to allow for unsafe writes. out.unsafeEnsure(fieldCnt << 3); int lastOffset = data[idx - 1]; int res; if (compactFooter) { if (lastOffset < MAX_OFFSET_1) { for (int curIdx = startIdx + 1; curIdx < idx; curIdx += 2) out.unsafeWriteByte((byte)data[curIdx]); res = BinaryUtils.OFFSET_1; } else if (lastOffset < MAX_OFFSET_2) { for (int curIdx = startIdx + 1; curIdx < idx; curIdx += 2) out.unsafeWriteShort((short) data[curIdx]); res = BinaryUtils.OFFSET_2; } else { for (int curIdx = startIdx + 1; curIdx < idx; curIdx += 2) out.unsafeWriteInt(data[curIdx]); res = BinaryUtils.OFFSET_4; } } else { if (lastOffset < MAX_OFFSET_1) { for (int curIdx = startIdx; curIdx < idx;) { out.unsafeWriteInt(data[curIdx++]); out.unsafeWriteByte((byte) data[curIdx++]); } res = BinaryUtils.OFFSET_1; } else if (lastOffset < MAX_OFFSET_2) { for (int curIdx = startIdx; curIdx < idx;) { out.unsafeWriteInt(data[curIdx++]); out.unsafeWriteShort((short) data[curIdx++]); } res = BinaryUtils.OFFSET_2; } else { for (int curIdx = startIdx; curIdx < idx;) { out.unsafeWriteInt(data[curIdx++]); out.unsafeWriteInt(data[curIdx++]); } res = BinaryUtils.OFFSET_4; } } return res; } /** * Pop current object's frame. */ public void pop(int fieldCnt) { idx = idx - fieldCnt * 2; } }