/** {@inheritDoc} */ @Override public void seek(long pos) throws IOException { if (pos < 0) throw new IOException("Seek position cannot be negative: " + pos); is.seek(start + pos); this.pos = pos; }
/** {@inheritDoc} */ @Override public void close() throws IOException { is.close(); }
/** * Constructor. * * @param is Base input stream. * @param start Start position. * @param maxLen Maximum stream length. * @throws IOException In case of exception. */ public GridGgfsRangeInputStream(GridGgfsInputStream is, long start, long maxLen) throws IOException { if (is == null) throw new IllegalArgumentException("Input stream cannot be null."); if (start < 0) throw new IllegalArgumentException("Start position cannot be negative."); if (start >= is.length()) throw new IllegalArgumentException("Start position cannot be greater that file length."); if (maxLen < 0) throw new IllegalArgumentException("Length cannot be negative."); if (start + maxLen > is.length()) throw new IllegalArgumentException("Sum of start position and length cannot be greater than file length."); this.is = is; this.start = start; this.maxLen = maxLen; is.seek(start); }
/** {@inheritDoc} */ @Override public int read() throws IOException { if (pos < maxLen) { int res = is.read(); if (res != -1) pos++; return res; } else return -1; }
/** {@inheritDoc} */ @Override public long length() { return is.length(); }
/** {@inheritDoc} */ @Override public GridGgfsFileRange resolveRecords(GridGgfs ggfs, GridGgfsInputStream stream, GridGgfsFileRange suggestedRecord) throws GridException, IOException { long suggestedStart = suggestedRecord.start(); long suggestedEnd = suggestedStart + suggestedRecord.length(); GridBiTuple<State, Delimiter> firstDelim = findFirstDelimiter(stream, suggestedStart); State state = firstDelim != null ? firstDelim.getKey() : new State(); Delimiter curDelim = firstDelim.getValue(); while (curDelim != null && curDelim.end < suggestedStart) curDelim = nextDelimiter(stream, state); if (curDelim != null && (curDelim.end >= suggestedStart && curDelim.end < suggestedEnd) || suggestedStart == 0 ) { // We found start delimiter. long start = suggestedStart == 0 ? 0 : curDelim.end; if (curDelim == null || curDelim.end < suggestedEnd) { GridBiTuple<State, Delimiter> lastDelim = findFirstDelimiter(stream, suggestedEnd); state = lastDelim != null ? firstDelim.getKey() : new State(); curDelim = lastDelim.getValue(); while (curDelim != null && curDelim.end < suggestedEnd) curDelim = nextDelimiter(stream, state); } long end = curDelim != null ? curDelim.end : stream.position(); return new GridGgfsFileRange(suggestedRecord.path(), start, end - start); } else // We failed to find any delimiters up to the EOS. return null; }
/** {@inheritDoc} */ @Override public int read(@NotNull byte[] b, int off, int len) throws IOException { if (pos < maxLen) { len = (int)Math.min(len, maxLen - pos); int res = is.read(b, off, len); if (res != -1) pos += res; return res; } else return -1; }
/** {@inheritDoc} */ @Override public GridGgfsFileRange resolveRecords(GridGgfs ggfs, GridGgfsInputStream stream, GridGgfsFileRange suggestedRecord) throws GridException, IOException { long suggestedEnd = suggestedRecord.start() + suggestedRecord.length(); long startRem = suggestedRecord.start() % recLen; long endRem = suggestedEnd % recLen; long start = Math.min(suggestedRecord.start() + (startRem != 0 ? (recLen - startRem) : 0), stream.length()); long end = Math.min(suggestedEnd + (endRem != 0 ? (recLen - endRem) : 0), stream.length()); assert end >= start; return start != end ? new GridGgfsFileRange(suggestedRecord.path(), start, end - start) : null; }
/** * Find first delimiter. In order to achieve this we have to rewind the stream until we find the delimiter * which stands at least [maxDelimLen] from the start search position or until we faced stream start. * Otherwise we cannot be sure that delimiter position is determined correctly. * * @param stream GGFS input stream. * @param startPos Start search position. * @return The first found delimiter. * @throws IOException In case of IO exception. */ @Nullable private GridBiTuple<State, Delimiter> findFirstDelimiter(GridGgfsInputStream stream, long startPos) throws IOException { State state; Delimiter delim; long curPos = Math.max(0, startPos - maxDelimLen); while (true) { stream.seek(curPos); state = new State(); delim = nextDelimiter(stream, state); if (curPos == 0 || delim == null || delim.start - curPos > maxDelimLen - 1) break; else curPos = Math.max(0, curPos - maxDelimLen); } return F.t(state, delim); }
/** {@inheritDoc} */ @Override public final Object execute(GridGgfs ggfs, GridGgfsFileRange range, GridGgfsInputStream in) throws GridException, IOException { in.seek(range.start()); return execute(ggfs, new GridGgfsRangeInputStream(in, range)); }
/** {@inheritDoc} */ @Override public Object execute() throws GridException { GridGgfs ggfs = grid.ggfs(ggfsName); try (GridGgfsInputStream in = ggfs.open(path)) { GridGgfsFileRange split = new GridGgfsFileRange(path, start, len); if (rslvr != null) { split = rslvr.resolveRecords(ggfs, in, split); if (split == null) { log.warning("No data found for split on local node after resolver is applied " + "[ggfsName=" + ggfsName + ", path=" + path + ", start=" + start + ", len=" + len + ']'); return null; } } in.seek(split.start()); return job.execute(ggfs, new GridGgfsFileRange(path, split.start(), split.length()), in); } catch (IOException e) { throw new GridException("Failed to execute GGFS job for file split [ggfsName=" + ggfsName + ", path=" + path + ", start=" + start + ", len=" + len + ']', e); } }
/** {@inheritDoc} */ @Override public Object execute() throws GridException { GridGgfs ggfs = grid.ggfs(ggfsName); try (GridGgfsInputStream in = ggfs.open(path)) { GridGgfsFileRange split = new GridGgfsFileRange(path, start, len); if (rslvr != null) { split = rslvr.resolveRecords(ggfs, in, split); if (split == null) { log.warning("No data found for split on local node after resolver is applied " + "[ggfsName=" + ggfsName + ", path=" + path + ", start=" + start + ", len=" + len + ']'); return null; } } in.seek(split.start()); return job.execute(ggfs, new GridGgfsFileRange(path, split.start(), split.length()), in); } catch (IOException e) { throw new GridException("Failed to execute GGFS job for file split [ggfsName=" + ggfsName + ", path=" + path + ", start=" + start + ", len=" + len + ']', e); } }