/******************************************************************************* * Copyright (c) 2011, 2014 Google, Inc and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Sergey Prigogin (Google) - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.core.parser.scanner; import java.util.Arrays; import java.util.Comparator; import org.eclipse.cdt.core.parser.ISignificantMacros; import org.eclipse.cdt.core.parser.util.CharArrayObjectMap; import org.eclipse.cdt.core.parser.util.CharArrayUtils; /** * A set of static methods to encode Map<String, String> as an array of characters and to decode * it back. * * The map is encoded as: * {@code <number_of_entries>,<key1><value1>...<keyN><valueN>}. * <p> * Each string is encoded as: {@code <number_of_characters>,<characters>}. * A {@code null} string is encoded as a single comma. */ public class SignificantMacros implements ISignificantMacros { public static final char[] DEFINED = {0}; public static final char[] UNDEFINED = {1}; private static final Comparator<char[]> SORTER = new Comparator<char[]>() { @Override public int compare(char[] s1, char[] s2) { return CharArrayUtils.compare(s1, s2); } }; private final char[] fEncoded; private int fHash; public SignificantMacros(char[] encoded) { assert encoded != null; fEncoded= encoded; } public SignificantMacros(CharArrayObjectMap<char[]> sigMacros) { fEncoded= encode(sigMacros); } private char[] encode(CharArrayObjectMap<char[]> sigMacros) { StringBuilder buffer= new StringBuilder(); char[][] keys= sigMacros.keys(); Arrays.sort(keys, SORTER); for (char[] name : keys) { char[] value= sigMacros.get(name); buffer.append((char) name.length).append(name); buffer.append((char) value.length).append(value); } int len= buffer.length(); char[] result= new char[len]; buffer.getChars(0, len, result, 0); return result; } @Override public int hashCode() { int h = fHash; if (h == 0) { char val[] = fEncoded; int len = fEncoded.length; for (int i = 0; i < len; i++) { h = 31*h + val[i]; } fHash = h; } return h; } @Override public boolean equals(Object obj) { return obj instanceof SignificantMacros && hashCode() == obj.hashCode() && CharArrayUtils.equals(fEncoded, ((SignificantMacros) obj).fEncoded); } @Override public boolean accept(IVisitor visitor) { final char[] encoded = fEncoded; final int len = encoded.length; int i= 0; while (i < len) { final int len1 = encoded[i++]; int v= i + len1; if (v >= len) break; final int len2 = encoded[v++]; if (v+len2 > len) break; char[] macro= extract(encoded, i, len1); i= v+len2; if (len2 == 1) { if (encoded[v] == UNDEFINED[0]) { if (!visitor.visitUndefined(macro)) return false; continue; } if (encoded[v] == DEFINED[0]) { if (!visitor.visitDefined(macro)) return false; continue; } } final char[] value = extract(encoded, v, len2); if (!visitor.visitValue(macro, value)) return false; } return true; } public char[] extract(final char[] source, int from, final int length) { char[] value= new char[length]; System.arraycopy(source, from, value, 0, length); return value; } @Override public char[] encode() { return fEncoded; } /** * For debugging purposes. */ @SuppressWarnings("nls") @Override public String toString() { final StringBuilder buf= new StringBuilder(); buf.append('{'); accept(new IVisitor() { @Override public boolean visitValue(char[] macro, char[] value) { buf.append(macro).append('=').append(value).append(','); return true; } @Override public boolean visitUndefined(char[] macro) { buf.append(macro).append('=').append("null,"); return true; } @Override public boolean visitDefined(char[] macro) { buf.append(macro).append('=').append("*,"); return true; } }); int buflen = buf.length(); if (buflen > 1) buf.setLength(buflen-1); buf.append('}'); return buf.toString(); } public static char[] shortenValue(char[] expansion) { if (expansion.length <= 16) return expansion; char[] result= new char[16]; System.arraycopy(expansion, 0, result, 0, 8); StreamHasher hasher= new StreamHasher(); hasher.addChunk(expansion); long hash= hasher.computeHash(); for(int i= 0; i<8; i++) { result[8+i]= (char) (hash & 0xff); hash= hash >> 1; } return result; } }