/* * 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. */ package com.addthis.hydra.data.tree; import java.util.HashMap; import java.util.Map; import java.nio.charset.Charset; import com.addthis.basis.util.Varint; import com.addthis.codec.annotations.FieldConfig; import com.addthis.codec.codables.BytesCodable; import com.addthis.codec.codables.ConcurrentCodable; import com.addthis.codec.codables.SuperCodable; import com.addthis.codec.reflection.Fields; import com.addthis.hydra.store.kv.PageEncodeType; import com.google.common.primitives.Ints; import com.fasterxml.jackson.annotation.JsonProperty; import io.netty.buffer.ByteBuf; import io.netty.buffer.PooledByteBufAllocator; import io.netty.buffer.Unpooled; public abstract class AbstractTreeNode implements DataTreeNode, SuperCodable, ConcurrentCodable, BytesCodable { public static final int ALIAS = 1 << 1; @FieldConfig(codable = true) protected long hits; @FieldConfig(codable = true) protected int nodes; @FieldConfig(codable = true) protected Integer nodedbLegacy; @FieldConfig(codable = true) protected int bits; @SuppressWarnings("unchecked") @FieldConfig(codable = true) protected HashMap<String, TreeNodeData> data; @JsonProperty //for :+json protected volatile long nodedb; @Override public byte[] bytesEncode(long version) { byte[] returnBytes; ByteBuf b = PooledByteBufAllocator.DEFAULT.buffer(); encodeLock(); try { Varint.writeUnsignedVarLong(hits, b); PageEncodeType.writeNodeId(b, version, nodedb); if ((data != null) && !data.isEmpty()) { int numAttachments = data.size(); Varint.writeSignedVarInt(numAttachments, b); for (Map.Entry<String, TreeNodeData> entry : data.entrySet()) { byte[] keyBytes = entry.getKey().getBytes(Charset.forName("UTF-8")); Varint.writeUnsignedVarInt(keyBytes.length, b); b.writeBytes(keyBytes); String classInfo = Fields.getClassFieldMap(entry.getValue().getClass()).getClassName(entry.getValue()); byte[] classNameBytes = classInfo.getBytes(Charset.forName("UTF-8")); Varint.writeUnsignedVarInt(classNameBytes.length, b); b.writeBytes(classNameBytes); byte[] bytes = entry.getValue().bytesEncode(version); Varint.writeUnsignedVarInt(bytes.length, b); b.writeBytes(bytes); } } else { Varint.writeSignedVarInt(-1, b); } if (hasNodes()) { Varint.writeUnsignedVarInt(nodes, b); Varint.writeUnsignedVarInt(bits, b); } returnBytes = new byte[b.readableBytes()]; b.readBytes(returnBytes); } catch (Exception e) { throw new RuntimeException(e); } finally { b.release(); encodeUnlock(); } return returnBytes; } @Override public void bytesDecode(byte[] b, long version) { ByteBuf buf = Unpooled.wrappedBuffer(b); try { hits = Varint.readUnsignedVarLong(buf); nodedb = PageEncodeType.readNodeId(buf, version); int numAttachments = Varint.readSignedVarInt(buf); if (numAttachments > 0) { HashMap<String, TreeNodeData> dataMap = new HashMap<>(); for (int i = 0; i < numAttachments; i++) { int kl = Varint.readUnsignedVarInt(buf); if (kl == 0) { continue; } String key = new String(buf.readBytes(kl).array(), Charset.forName("UTF-8")); int cl = Varint.readUnsignedVarInt(buf); String className = new String(buf.readBytes(cl).array(), Charset.forName("UTF-8")); TreeNodeData tn = (TreeNodeData) Fields.getClassFieldMap(TreeNodeData.class).getClass(className).newInstance(); int vl = Varint.readUnsignedVarInt(buf); tn.bytesDecode(buf.readBytes(vl).array(), version); dataMap.put(key, tn); } data = dataMap; } if (hasNodes()) { nodes = Varint.readUnsignedVarInt(buf); bits = Varint.readUnsignedVarInt(buf); } postDecode(); } catch (Exception e) { throw new RuntimeException(e); } finally { buf.release(); } } @Override public void postDecode() { if (nodedbLegacy != null) { nodedb = nodedbLegacy.longValue(); } if (data != null) { for (TreeNodeData actor : data.values()) { actor.setBoundNode(this); } } } @Override public void preEncode() { if (hasNodes()) { nodedbLegacy = Ints.checkedCast(nodedb); } } @Override public void encodeLock() {} @Override public void encodeUnlock() {} public boolean hasNodes() { return nodedb > 0; } @Override public Map<String, TreeNodeData> getDataMap() { return data; } }