1 package org.simantics.graph.representation;
3 import java.io.DataInput;
4 import java.io.DataInputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.nio.ByteBuffer;
9 import java.nio.channels.ReadableByteChannel;
10 import java.util.ArrayList;
11 import java.util.List;
13 import org.simantics.databoard.Bindings;
14 import org.simantics.databoard.Datatypes;
15 import org.simantics.databoard.binding.error.RuntimeDatatypeConstructionException;
16 import org.simantics.databoard.binding.mutable.Variant;
17 import org.simantics.databoard.container.DataContainer;
18 import org.simantics.databoard.container.DataContainers;
19 import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;
20 import org.simantics.databoard.serialization.Serializer;
21 import org.simantics.databoard.type.Datatype;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
27 * It is recommended to use {@link #read(File)} and {@link #read(InputStream)}
28 * for reading to ensure proper resource handling.
30 final public class TransferableGraphFileReader extends ByteFileReader {
32 private static final Logger LOGGER = LoggerFactory.getLogger(TransferableGraphFileReader.class);
34 InputStream in = new InputStream() {
37 public int read() throws IOException {
42 public int read(byte[] b) throws IOException {
43 // FIXME not correctly implemented
44 System.arraycopy(safeBytes(b.length), 0, b, 0, b.length);
49 public int read(byte[] b, int off, int len) throws IOException {
50 // FIXME not correctly implemented
51 System.arraycopy(safeBytes(len), 0, b, off, len);
58 final static class InputChannel implements ReadableByteChannel {
60 final private InputStream stream;
62 public InputChannel(InputStream stream) {
67 public boolean isOpen() {
72 public void close() throws IOException {
73 // NOTE: it is an intentional choice not to close the underlying stream here
74 // InputStreams given directly to TransferableGraphFileReader are expected to
75 // be closed outside of this implementation.
79 public int read(ByteBuffer dst) throws IOException {
82 int position = dst.position();
83 int limit = dst.limit();
84 // The users of this channel expect that the data is fully read at this point
85 while ((nRead = stream.read(dst.array(), position, limit - position)) != -1 && limit - position > 0) {
94 private static boolean init = true;
96 final private static int SIZE = 1<<18;
97 final private static int HEADER = headerSize();
98 final private int header;
101 * Reads a {@link DataContainer} containing a {@link TransferableGraph1}
102 * structure from the specified {@link File}.
104 * @param file the file to read from
105 * @return the TG contained by the file
106 * @throws IOException
108 public static TransferableGraph1 read(File file) throws IOException {
109 try (TransferableGraphFileReader reader = new TransferableGraphFileReader(file)) {
110 return reader.readTG();
115 * Reads a {@link DataContainer} containing a {@link TransferableGraph1}
116 * structure from the specified InputStream. Note that this implementation does
117 * not close the specified <code>input</code> stream, it is expected to be
118 * closed by the caller.
120 * @param input the input stream to read from
121 * @return the TG contained by the stream
122 * @throws IOException
124 public static TransferableGraph1 read(InputStream input) throws IOException {
125 try (TransferableGraphFileReader reader = new TransferableGraphFileReader(input)) {
126 return reader.readTG();
130 public TransferableGraphFileReader(File file) throws IOException {
134 TransferableGraphFileReader r = new TransferableGraphFileReader(file, 0);
135 for(int i=0;i<40000;i++) r.readTG();
137 this.header = HEADER;
140 public TransferableGraphFileReader(InputStream stream) throws IOException {
141 super(null, new InputChannel(stream), SIZE);
144 TransferableGraphFileReader r = new TransferableGraphFileReader(stream, 0);
145 for(int i=0;i<40000;i++) r.readTG();
150 public TransferableGraphFileReader(ReadableByteChannel channel) throws IOException {
151 super(null, channel, SIZE);
154 TransferableGraphFileReader r = new TransferableGraphFileReader(channel, 0);
155 for(int i=0;i<40000;i++) r.readTG();
160 public TransferableGraphFileReader(ReadableByteChannel channel, int size) throws IOException {
161 super(null, channel, SIZE);
165 public TransferableGraphFileReader(InputStream stream, int size) throws IOException {
166 super(null, new InputChannel(stream), size);
170 public TransferableGraphFileReader(File file, int size) throws IOException {
172 this.header = HEADER;
175 private static int headerSize() {
177 return Bindings.getSerializerUnchecked(Datatype.class).serialize(Datatypes.getDatatypeUnchecked(TransferableGraph1.class)).length;
178 } catch (RuntimeSerializerConstructionException e) {
179 throw new Error("Failed to determine TransferableGraph1 header size. ", e);
180 } catch (RuntimeDatatypeConstructionException e) {
181 throw new Error("Failed to determine TransferableGraph1 header size. ", e);
182 } catch (IOException e) {
183 throw new Error("Failed to determine TransferableGraph1 header size. ", e);
187 public TransferableGraph1 readTG() throws IOException {
189 if(getSize() == 0) return null;
191 // long start = System.nanoTime();
193 final byte[] bytes = getBytes();
195 // byteIndex = header;
197 DataInputStream dis = new DataInputStream(in);
200 DataContainers.readHeader(dis);
202 // Content variant data type
203 Bindings.getSerializerUnchecked(Datatype.class).deserialize((DataInput)dis);
205 int resourceCount = safeInt();
207 List<Object> idcontext = new ArrayList<>();
208 dis = new DataInputStream(in);
209 Extensions extensions = (Extensions)Bindings.getSerializerUnchecked(Extensions.class).deserialize((DataInput)dis, idcontext);
211 int identities = safeInt();
212 Identity[] ids = new Identity[identities];
214 // LOGGER.warn("rc: " + resourceCount);
215 // LOGGER.warn("ids: " + identities);
217 // long duration = System.nanoTime() - start;
218 // LOGGER.warn("start in " + 1e-9*duration + "s.");
219 // start = System.nanoTime();
221 for(int i=0;i<identities;i++) {
223 byte type = bytes[byteIndex++];
227 int parent = safeInt();
228 int nameLen = getDynamicUInt32();
230 if(byteIndex+nameLen < SIZE) {
231 ids[i] = new Identity(rid, new External(parent, utf(bytes, byteIndex, byteIndex + nameLen)));
232 byteIndex += nameLen;
234 ids[i] = new Identity(rid, new External(parent, utf(safeBytes(nameLen), 0, nameLen)));
241 int parent = safeInt();
242 int nameLen = getDynamicUInt32();
243 if(byteIndex+nameLen < SIZE) {
244 ids[i] = new Identity(rid, new Internal(parent, utf(bytes, byteIndex, byteIndex + nameLen)));
245 byteIndex += nameLen;
247 ids[i] = new Identity(rid, new Internal(parent, utf(safeBytes(nameLen), 0, nameLen)));
253 int nameLen = getDynamicUInt32();
254 String name = utf(safeBytes(nameLen), 0, nameLen);
255 int nameLen2 = getDynamicUInt32();
256 String rType = utf(safeBytes(nameLen2), 0, nameLen2);
257 ids[i] = new Identity(rid, new Root(name, rType));
259 } else if(type == 2) {
260 throw new UnsupportedOperationException();
262 throw new IllegalStateException();
267 // for(Identity id : ids) LOGGER.warn("id: " + id);
270 // duration = System.nanoTime() - start;
271 // LOGGER.warn("ids in " + 1e-9*duration + "s.");
272 // start = System.nanoTime();
274 int stmLength = safeInt();
275 // LOGGER.warn("statements: " + stmLength + " (" + byteIndex + ")");
277 int[] statements = new int[stmLength];
279 for(int stmIndex=0;stmIndex<stmLength;) {
281 statements[stmIndex++] = safeInt();
284 int avail = (SIZE-byteIndex) >> 2;
285 int allowed = Math.min(stmLength-stmIndex, avail);
286 for(int index = byteIndex, i=0;i<allowed;i++) {
287 statements[stmIndex++] = ((bytes[index++]&0xff)<<24) | ((bytes[index++]&0xff)<<16) | ((bytes[index++]&0xff)<<8) | ((bytes[index++]&0xff));
289 byteIndex += allowed<<2;
293 // duration = System.nanoTime() - start;
294 // LOGGER.warn("stms in " + 1e-9*duration + "s.");
296 // start = System.nanoTime();
298 int valueLength = safeInt();
299 // LOGGER.warn("values: " + valueLength + " (" + byteIndex + ")");
301 Value[] values = new Value[valueLength];
303 Serializer variantSerializer = Bindings.getSerializerUnchecked(Bindings.VARIANT);
305 dis = new DataInputStream(in);
307 for(int i=0;i<valueLength;i++) {
308 int resource = safeInt();
310 Variant value = (Variant)variantSerializer
311 .deserialize((DataInput)dis, idcontext);
312 values[i] = new Value(resource, value);
314 //LOGGER.warn("read variant[" + resource + "]: " + value.toString().substring(0, Math.min(100, value.toString().length())));
319 // duration = System.nanoTime() - start;
320 // LOGGER.warn("values in " + 1e-9*duration + "s.");
322 return new TransferableGraph1(resourceCount, ids, statements, values, extensions.map);
326 public static void main(String[] args) {
330 File file = new File("c:/users/antti villberg/desktop/test.apros");
331 TransferableGraphFileReader reader = new TransferableGraphFileReader(file, SIZE);
332 reader = new TransferableGraphFileReader(file);
333 long s = System.nanoTime();
335 long d = System.nanoTime() - s;
336 LOGGER.warn("Duration=" + 1e-9*d + "s.");
339 } catch (Throwable t) {