/******************************************************************************* * Copyright (c) 2013, 2014 Institute for Software, HSR Hochschule fuer Technik * Rapperswil, University of applied sciences. * 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: * Thomas Corbat (IFS) - Initial API and implementation * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.pdom.dom.cpp; import org.eclipse.cdt.core.dom.IPDOMVisitor; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.internal.core.index.IIndexFragmentBinding; import org.eclipse.cdt.internal.core.pdom.PDOM; import org.eclipse.cdt.internal.core.pdom.db.Database; import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage; import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode; import org.eclipse.core.runtime.CoreException; /** * PDOMCPPMemberBlock stores the members of a composite type and maps every member to * the corresponding visibility. */ public class PDOMCPPMemberBlock { /* * The MAX_MEMBER_COUNT was chosen empirically by comparing PDOM file sizes of a real-life * project. Six members per block resulted in the most compact PDOM. */ private static final int MAX_MEMBER_COUNT = 6; private static final int VISIBILITY_BITS = 2; private static final int VISIBILITY_MASK = (1 << VISIBILITY_BITS) - 1; private static final int VISIBILITY_VALUES_PER_BYTE = 8 / VISIBILITY_BITS; private static final int MEMBER_POINTERS = 0; private static final int MEMBER_VISIBILITIES = MEMBER_POINTERS + Database.PTR_SIZE * MAX_MEMBER_COUNT; private static final int NEXT_MEMBER_BLOCK = MEMBER_VISIBILITIES + (MAX_MEMBER_COUNT + VISIBILITY_VALUES_PER_BYTE - 1) / VISIBILITY_VALUES_PER_BYTE; protected static final int RECORD_SIZE = NEXT_MEMBER_BLOCK + Database.PTR_SIZE; static { assert (MAX_MEMBER_COUNT > 0); } private final PDOMLinkage linkage; private final long record; private int nextMemberPosition; public PDOMCPPMemberBlock(PDOMLinkage linkage, long record) throws CoreException { this.linkage = linkage; this.record = record; this.nextMemberPosition = -1; // nextMemberPosition is unknown. } public PDOMCPPMemberBlock(PDOMLinkage linkage) throws CoreException { Database db = linkage.getDB(); this.linkage = linkage; this.record = db.malloc(RECORD_SIZE); } private int getNextPosition() throws CoreException { if (nextMemberPosition < 0) { nextMemberPosition = 0; while (nextMemberPosition < MAX_MEMBER_COUNT && getMemberRecord(nextMemberPosition) != 0) { nextMemberPosition++; } } return nextMemberPosition; } private Database getDB() { return linkage.getDB(); } private PDOM getPDOM() { return linkage.getPDOM(); } public long getRecord() { return record; } public void setNextBlock(PDOMCPPMemberBlock nextBlock) throws CoreException { long rec = nextBlock != null ? nextBlock.getRecord() : 0; getDB().putRecPtr(record + NEXT_MEMBER_BLOCK, rec); } public PDOMCPPMemberBlock getNextBlock() throws CoreException { long rec = getDB().getRecPtr(record + NEXT_MEMBER_BLOCK); return rec != 0 ? new PDOMCPPMemberBlock(linkage, rec) : null; } public void addMember(PDOMNode member, int visibility) throws CoreException { addMember(this, member, visibility); } private static void addMember(PDOMCPPMemberBlock block, PDOMNode member, int visibility) throws CoreException { assert member.getPDOM() == block.getPDOM(); while (true) { int pos = block.getNextPosition(); if (pos < MAX_MEMBER_COUNT) { long memberLocationOffset = block.getMemberOffset(pos); block.getDB().putRecPtr(memberLocationOffset, member.getRecord()); block.setVisibility(pos, visibility); block.nextMemberPosition++; break; } PDOMCPPMemberBlock previousBlock = block; block = block.getNextBlock(); if (block == null) { block = new PDOMCPPMemberBlock(previousBlock.linkage); previousBlock.setNextBlock(block); } } } public void accept(IPDOMVisitor visitor) throws CoreException { PDOMCPPMemberBlock current = this; do { current.visitBlock(visitor); } while ((current = current.getNextBlock()) != null); } private void visitBlock(IPDOMVisitor visitor) throws CoreException { if (record == 0) { throw new NullPointerException(); } int item = 0; long memberRecord; while (item < MAX_MEMBER_COUNT && (memberRecord = getMemberRecord(item++)) != 0) { PDOMNode node = PDOMNode.load(getPDOM(), memberRecord); if (node != null) { if (visitor.visit(node)) node.accept(visitor); visitor.leave(node); } } } public void delete() throws CoreException { getDB().free(record); } private long getMemberRecord(int memberIndex) throws CoreException { return getDB().getRecPtr(getMemberOffset(memberIndex)); } private long getMemberOffset(int memberIndex) { return record + MEMBER_POINTERS + Database.PTR_SIZE * memberIndex; } private void setVisibility(int memberIndex, int newVisibility) throws CoreException { newVisibility &= VISIBILITY_MASK; int visibilityBitOffset = memberIndex % VISIBILITY_VALUES_PER_BYTE; long visibilityOffset = record + MEMBER_VISIBILITIES + memberIndex / VISIBILITY_VALUES_PER_BYTE; int visibility = getDB().getByte(visibilityOffset); // Resetting the previous visibility bits of the target member. visibility &= ~(VISIBILITY_MASK << visibilityBitOffset * VISIBILITY_BITS); // Setting the new visibility bits of the target member. visibility |= newVisibility << visibilityBitOffset * VISIBILITY_BITS; getDB().putByte(visibilityOffset, (byte) visibility); } /** * Returns visibility of the member, or -1 if the given binding is not a member. */ public int getVisibility(IBinding member) throws CoreException { IIndexFragmentBinding indexMember = getPDOM().adaptBinding(member); if (!(indexMember instanceof PDOMNode)) return -1; return getVisibility(this, (PDOMNode) indexMember); } private static int getVisibility(PDOMCPPMemberBlock block, PDOMNode memberNode) throws CoreException { long memberRecord = memberNode.getRecord(); do { for (int memberIndex = 0; memberIndex < MAX_MEMBER_COUNT; memberIndex++) { long rec = block.getMemberRecord(memberIndex); if (rec == 0) return -1; if (rec == memberRecord) return block.getVisibility(memberIndex); } } while ((block = block.getNextBlock()) != null); return -1; } private int getVisibility(int memberIndex) throws CoreException { int visibilityBitOffset = memberIndex % VISIBILITY_VALUES_PER_BYTE; long visibilityOffset = record + MEMBER_VISIBILITIES + memberIndex / VISIBILITY_VALUES_PER_BYTE; int visibility = getDB().getByte(visibilityOffset); visibility >>>= visibilityBitOffset * VISIBILITY_BITS; // Filtering the visibility bits of the target member. visibility &= VISIBILITY_MASK; return visibility; } }