/** * Copyright 2014 National University of Ireland, Galway. * * This file is part of the SIREn project. Project and contact information: * * https://github.com/rdelbru/SIREn * * 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 org.sindice.siren.index.codecs.block; import java.io.File; import java.io.FileWriter; import java.io.IOException; /** * This class is used to generate {@link AForFrameCompressor}. */ public class AForFrameCompressorGenerator { private FileWriter writer; private final int[] frameSizes = new int[99]; public AForFrameCompressorGenerator() throws IOException { } public void generate(final File file) throws IOException { writer = new FileWriter(file); writer.append(FILE_HEADER); writer.append('\n'); this.generateClass(); writer.close(); } protected void generateClass() throws IOException { writer.append("/**\n"); writer.append(" * This class contains a lookup table of functors for compressing fames.\n"); writer.append(" */\n"); writer.append("public class AForFrameCompressor {\n\n"); this.generateFrameSizeTable(); this.generateTable(); this.generateAbstractInnerClass(); for (int i = 0; i < 99; i++) { this.generateInnerClass(i); } writer.append("}\n"); } protected void generateFrameSizeTable() throws IOException { int frameSize = 0; frameSizes[0] = 0; for (int i = 1; i <= 32; i++) { frameSize += 4; frameSizes[i] = frameSize; } frameSizes[33] = 0; frameSize = 0; for (int i = 1; i <= 32; i++) { frameSize += 2; frameSizes[i+33] = frameSize; } frameSizes[66] = 0; for (int i = 1; i <= 32; i++) { frameSizes[i+66] = i; } } protected void generateTable() throws IOException { writer.append(" public static final FrameCompressor[] compressors = new FrameCompressor[] {\n"); for (int i = 0; i < 99; i++) { writer.append(" new FrameCompressor"+i+"(),\n"); } writer.append(" };\n\n"); } protected void generateAbstractInnerClass() throws IOException { writer.append(" static abstract class FrameCompressor {\n"); writer.append(" public abstract void compress(final IntsRef input, final BytesRef output);\n"); writer.append(" }\n\n"); } protected void generateInnerClass(final int numFramebits) throws IOException { writer.append(" static final class FrameCompressor" + Integer.toString(numFramebits) + " extends FrameCompressor {\n"); this.generateMethod(numFramebits); writer.append(" }\n\n"); } protected void generateMethodHeader() throws IOException { writer.append(" "); writer.append("final int[] unCompressedData = input.ints;\n"); writer.append(" "); writer.append("final byte[] compressedArray = output.bytes;\n"); writer.append(" "); writer.append("final int outputOffset = output.offset + 1;\n"); writer.append(" "); writer.append("final int offset = input.offset;\n"); } protected void generateMethodFooter(final int numFramebits) throws IOException { writer.append(" "); if (numFramebits <= 32) writer.append("input.offset += 32;\n"); else if (numFramebits <= 65) writer.append("input.offset += 16;\n"); else writer.append("input.offset += 8;\n"); writer.append(" "); writer.append("output.offset += " + (1 + frameSizes[numFramebits]) + ";\n"); } protected void generateMethod(final int numFrameBits) throws IOException { writer.append(" public final void compress(final IntsRef input, final BytesRef output) {\n"); if (numFrameBits == 0 || numFrameBits == 33 || numFrameBits == 66) { writer.append(" "); writer.append("output.offset += 1;\n"); writer.append(" "); writer.append("input.offset += " + (numFrameBits == 0 ? 32 : (numFrameBits == 33 ? 16 : 8)) + ";\n"); writer.append(" }\n"); return; } this.generateMethodHeader(); if (numFrameBits <= 32) this.generateIntValues(32); else if (numFrameBits <= 65) this.generateIntValues(16); else this.generateIntValues(8); if (numFrameBits <= 32) { if (numFrameBits <= 8) { this.generateInstructions1to8(numFrameBits, 32); } else { this.generateInstructions9to32(numFrameBits, 32); } } else if (numFrameBits <= 65) { if (numFrameBits - 33 <= 8) { this.generateInstructions1to8(numFrameBits - 33, 16); } else { this.generateInstructions9to32(numFrameBits - 33, 16); } } else { if (numFrameBits - 66 <= 8) { this.generateInstructions1to8(numFrameBits - 66, 8); } else { this.generateInstructions9to32(numFrameBits - 66, 8); } } this.generateMethodFooter(numFrameBits); writer.append(" }\n"); } protected void generateIntValues(final int nb) throws IOException { for (int i = 0; i < nb; i += 2) { writer.append(" "); writer.append("final int intValues"+i+" = unCompressedData[offset + "+i+"], "); writer.append("intValues"+(i+1)+" = unCompressedData[offset + "+(i+1)+"];\n"); } writer.append("\n"); } protected void generateInstructions1to8(final int numFrameBits, final int nb) throws IOException { int intPtr = 0; int bytePtr = 0; boolean first = true; int shift = 8; while (intPtr != nb) { // while we didn't process 32 integers while (shift >= numFrameBits) { // while shift is inferior to numFrameBits, shift -= numFrameBits; if (!first) { // if not first instruction, just add indentation and logic or writer.append("\n | "); } else { // if first instruction, add byte array assignment writer.append(" compressedArray[outputOffset + "+bytePtr+"] = (byte) ("); first = false; } if (shift == 0) { // if shift == 0, we do not have to add it writer.append("(intValues"+intPtr+" & "+((1 << numFrameBits) - 1)+")"); } else { writer.append("((intValues"+intPtr+" & "+((1 << numFrameBits) - 1)+") << "+shift+")"); } intPtr++; } if (shift > 0) { // when shift is inferior to numFrameBits and not equal to 0, special case. final int remainingBits = numFrameBits - shift; writer.append("\n | "); writer.append("((intValues"+intPtr+" >>> "+remainingBits+") & 0xFF));\n"); bytePtr++; shift = 8 - remainingBits; writer.append(" compressedArray[outputOffset + "+bytePtr+"] = (byte) ("); writer.append("((intValues"+intPtr+" & "+((1 << remainingBits) - 1)+") << "+shift+")"); intPtr++; } else { writer.append(");\n"); bytePtr++; shift = 8; first = true; } } } protected void generateInstructions9to32(final int numFrameBits, final int nb) throws IOException { int intPtr = 0; int bytePtr = 0; int shift = numFrameBits; while (intPtr != nb) { // while we didn't process 32 integers while (shift > 8) { // while the shift is superior to the size of a byte shift -= 8; writer.append(" compressedArray[outputOffset + "+bytePtr+"] = (byte) ((intValues"+intPtr+" >>> "+shift+") & 0xFF);\n"); bytePtr++; // increment the byte pointer } // if shift == 8, we just have to copy the less significant byte of the integer if (shift == 8) { writer.append(" compressedArray[outputOffset + "+bytePtr+"] = (byte) (intValues"+intPtr+" & 0xFF);\n"); bytePtr++; intPtr++; shift = numFrameBits; } else { // If the shift is less than 8 (size of byte), special case with two // integers. writer.append(" compressedArray[outputOffset + "+bytePtr+"] = (byte) ((intValues"+intPtr+" & "+((1 << shift) - 1)+") << "+(8 - shift)+"\n"); intPtr++; // increment the integer pointer for the next instruction writer.append(" | "); writer.append("(intValues"+intPtr+" >>> "+(numFrameBits - (8 - shift))+") & 0xFF);\n"); bytePtr++; // increment byte pointer shift = numFrameBits - (8 - shift); // set shift to numFrameBits minus the number of bits shifted in the last instruction } } } private static final String FILE_HEADER = "/**\n" + " * Copyright 2014 National University of Ireland, Galway.\n" + " *\n" + " * This file is part of the SIREn project. Project and contact information:\n" + " *\n" + " * https://github.com/rdelbru/SIREn\n" + " *\n" + " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" + " * you may not use this file except in compliance with the License.\n" + " * You may obtain a copy of the License at\n" + " *\n" + " * http://www.apache.org/licenses/LICENSE-2.0\n" + " *\n" + " * Unless required by applicable law or agreed to in writing, software\n" + " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + " * See the License for the specific language governing permissions and\n" + " * limitations under the License.\n" + " */ \n" + "/* This program is generated, do not modify. See AForFrameCompressorGenerator.java */\n" + "package org.sindice.siren.index.codecs.block;\n" + "\n" + "import org.apache.lucene.util.BytesRef;\n" + "import org.apache.lucene.util.IntsRef;\n"; /** * @param args * @throws IOException */ public static void main(final String[] args) throws IOException { final File file = new File("./src/main/java/org/sindice/siren/index/codecs/block", "AForFrameCompressor.java"); final AForFrameCompressorGenerator generator = new AForFrameCompressorGenerator(); generator.generate(file); } }