/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * See LICENSE.txt included in this distribution for the specific * language governing permissions and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at LICENSE.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. */ package org.opensolaris.opengrok.web; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Map; import java.util.StringTokenizer; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import org.opensolaris.opengrok.logger.LoggerFactory; /** * An Extremely Fast Tagged Attribute Read-only File System * Created on October 12, 2005 * * A Eftar File has the following format * FILE --> Record ( Record | tagString ) * * Record --> 64bit:Hash 16bit:childrenOffset 16bit:(numberChildren|lenthOfTag) 16bit:tagOffset * * It is a tree of tagged names, * doing binary search in sorted list of children * * @author Chandan */ public class EftarFile { private static final Logger LOGGER = LoggerFactory.getLogger(EftarFile.class); public static final int RECORD_LENGTH = 14; private long offset; class Node { public long hash; public String tag; public Map children; public long tagOffset; public long childOffset; public long myOffset; public Node(long hash, String tag) { this.hash = hash; this.tag = tag; children = new TreeMap<>(); } public Node put(long hash, String desc) { if (children.get(hash) == null) { children.put(hash, new Node(hash, desc)); } return children.get(hash); } public Node get(long hash) { return children.get(hash); } } class FNode { public long offset; public long hash; public int childOffset; public int numChildren; public int tagOffset; public FNode(RandomAccessFile f) throws Throwable { offset = f.getFilePointer(); hash = f.readLong(); childOffset = f.readUnsignedShort(); numChildren = f.readUnsignedShort(); tagOffset = f.readUnsignedShort(); } public FNode(long hash, long offset, int childOffset, int num, int tagOffset) { this.hash = hash; this.offset = offset; this.childOffset = childOffset; this.numChildren = num; this.tagOffset = tagOffset; } public FNode get(long hash, RandomAccessFile f) throws Throwable { if (childOffset == 0) { return null; } return sbinSearch(offset + childOffset, numChildren, hash, f); } private FNode sbinSearch(long start, int len, long hash, RandomAccessFile f) throws Throwable { int b = 0; int e = len; while (b <= e) { int m = (b + e) / 2; f.seek(start + m * RECORD_LENGTH); long mhash = f.readLong(); if (hash > mhash) { b = m + 1; } else if (hash < mhash) { e = m - 1; } else { return new FNode(mhash, f.getFilePointer() - 8l, f.readUnsignedShort(), f.readUnsignedShort(), f.readUnsignedShort()); } } return null; } } public static long myHash(String name) { if (name == null || name.length() == 0) { return 0; } long hash = 2861; int n = name.length(); if (n > 100) { n = 100; } for (int i = 0; i < n; i++) { hash = (hash * 641 + name.charAt(i) * 2969 + hash << 6) % 9322397; } return hash; } private void write(Node n, DataOutputStream out) throws IOException { if (n.tag != null) { out.write(n.tag.getBytes()); offset += n.tag.length(); } for (Node childnode : n.children.values()) { out.writeLong(childnode.hash); if (childnode.children.size() > 0) { out.writeShort((short) (childnode.childOffset - offset)); out.writeShort((short) childnode.children.size()); } else { out.writeShort(0); if (childnode.tag == null) { out.writeShort((short) 0); } else { out.writeShort((short) childnode.tag.length()); } } if (childnode.tag == null) { out.writeShort(0); } else { out.writeShort((short) (childnode.tagOffset - offset)); } offset += RECORD_LENGTH; } for (Node childnode : n.children.values()) { write(childnode, out); } } private void traverse(Node n) { if (n.tag == null) { n.tagOffset = 0; } else { n.tagOffset = offset; offset += n.tag.length(); } if (n.children.size() > 0) { n.childOffset = offset; offset += (RECORD_LENGTH * n.children.size()); } else { n.childOffset = 0; } for (Node childnode : n.children.values()) { traverse(childnode); } } private Node root; public void readInput(String tagsPath) throws IOException { try (BufferedReader r = new BufferedReader(new FileReader(tagsPath))) { readInput(r); } } private void readInput(BufferedReader r) throws IOException { if (root == null) { root = new Node(1, null); } String line; while ((line = r.readLine()) != null) { int tab = line.indexOf('\t'); if (tab > 0) { String path = line.substring(0, tab); String desc = line.substring(tab + 1); StringTokenizer toks = new StringTokenizer(path, "\\/"); Node n = root; while (toks.hasMoreTokens()) { n = n.put(myHash(toks.nextToken()), null); } n.tag = desc; } } } public void write(String outPath) throws FileNotFoundException, IOException { offset = RECORD_LENGTH; traverse(root); try (DataOutputStream out = new DataOutputStream( new BufferedOutputStream(new FileOutputStream(outPath)))) { out.writeLong(0x5e33); out.writeShort(RECORD_LENGTH); out.writeShort(root.children.size()); out.writeShort(0); offset = RECORD_LENGTH; write(root, out); } } public void create(String[] args) throws IOException, FileNotFoundException { for (int i = 0; i < args.length - 1; i++) { readInput(args[i]); } write(args[args.length - 1]); } /** * Main method is used to generate eftar file from the path description * file in the run scripts. * * @param args Input files and output file */ @SuppressWarnings("PMD.SystemPrintln") public static void main(String[] args) { if (args.length < 2) { System.err.println("Usage inputFile [inputFile ...] outputFile"); System.exit(1); } try { EftarFile ef = new EftarFile(); ef.create(args); } catch (IOException e) { LOGGER.log(Level.WARNING, "EftarFile error", e); } } }