blob: 0c3c4366b84428a07ddb27c297569106534ddf37 [file] [log] [blame]
/*
* 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.jackrabbit.oak.segment.file.tar.binaries;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.zip.CRC32;
import com.google.common.base.Charsets;
/**
* Maintains the transient state of a binary references index, formats it and
* serializes it.
*/
public class BinaryReferencesIndexWriter {
/**
* Create a new, empty instance of {@link BinaryReferencesIndexWriter}.
*
* @return An instance of {@link BinaryReferencesIndexWriter}.
*/
public static BinaryReferencesIndexWriter newBinaryReferencesIndexWriter() {
return new BinaryReferencesIndexWriter();
}
private final Map<Generation, Map<UUID, Set<String>>> entries;
private BinaryReferencesIndexWriter() {
entries = new HashMap<>();
}
/**
* Add an entry to the binary references index.
*
* @param generation The generation of the segment containing the
* reference.
* @param full The full generation of the segment containing the
* reference.
* @param compacted {@code true} if the segment containing the reference is
* created by a compaction operation.
* @param segment The identifier of the segment containing the
* reference.
* @param reference The binary reference.
*/
public void addEntry(int generation, int full, boolean compacted, UUID segment, String reference) {
entries
.computeIfAbsent(new Generation(generation, full, compacted), k -> new HashMap<>())
.computeIfAbsent(segment, k -> new HashSet<>())
.add(reference);
}
/**
* Write the current state of this instance to an array of bytes.
*
* @return An array of bytes containing the serialized state of the binary
* references index.
*/
public byte[] write() {
int binaryReferenceSize = 0;
// The following information are stored in the footer as meta-
// information about the entry.
// 4 bytes to store a magic number identifying this entry as containing
// references to binary values.
binaryReferenceSize += 4;
// 4 bytes to store the CRC32 checksum of the data in this entry.
binaryReferenceSize += 4;
// 4 bytes to store the length of this entry, without including the
// optional padding.
binaryReferenceSize += 4;
// 4 bytes to store the number of generations pairs in the binary
// references map.
binaryReferenceSize += 4;
// The following information are stored as part of the main content of
// this entry, after the optional padding.
for (Map<UUID, Set<String>> segmentToReferences : entries.values()) {
// 4 bytes per generation to store the generation number.
binaryReferenceSize += 4;
// 4 bytes per generation to store the full generation number.
binaryReferenceSize += 4;
// 1 byte per generation to store the "compacted" flag.
binaryReferenceSize += 1;
// 4 bytes per generation to store the number of segments.
binaryReferenceSize += 4;
for (Set<String> references : segmentToReferences.values()) {
// 16 bytes per segment identifier.
binaryReferenceSize += 16;
// 4 bytes to store the number of references for this segment.
binaryReferenceSize += 4;
for (String reference : references) {
// 4 bytes for each reference to store the length of the reference.
binaryReferenceSize += 4;
// A variable amount of bytes, depending on the reference itself.
binaryReferenceSize += reference.getBytes(Charsets.UTF_8).length;
}
}
}
ByteBuffer buffer = ByteBuffer.allocate(binaryReferenceSize);
for (Entry<Generation, Map<UUID, Set<String>>> be : entries.entrySet()) {
Generation generation = be.getKey();
Map<UUID, Set<String>> segmentToReferences = be.getValue();
buffer.putInt(generation.generation);
buffer.putInt(generation.full);
buffer.put((byte) (generation.compacted ? 1 : 0));
buffer.putInt(segmentToReferences.size());
for (Entry<UUID, Set<String>> se : segmentToReferences.entrySet()) {
UUID segmentId = se.getKey();
Set<String> references = se.getValue();
buffer.putLong(segmentId.getMostSignificantBits());
buffer.putLong(segmentId.getLeastSignificantBits());
buffer.putInt(references.size());
for (String reference : references) {
byte[] bytes = reference.getBytes(Charsets.UTF_8);
buffer.putInt(bytes.length);
buffer.put(bytes);
}
}
}
CRC32 checksum = new CRC32();
checksum.update(buffer.array(), 0, buffer.position());
buffer.putInt((int) checksum.getValue());
buffer.putInt(entries.size());
buffer.putInt(binaryReferenceSize);
buffer.putInt(BinaryReferencesIndexLoaderV2.MAGIC);
return buffer.array();
}
}