src/com/amazon/ion/impl/UnifiedSavePointManagerX.java (322 lines of code) (raw):

/* * Copyright 2007-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.amazon.ion.impl; /** * These classes (UnifiedSavePointManager and the contained * SavePoint) isolate the save point handing. Its effect spans * both the input stream and the underlying buffer - so the manager * keeps it's own reference to these. By doing so this should * make save points easier to use. It grabs the buffer from * the stream since it need a reference to the UnifiedInputBuffer * on most calls and doesn't need an extra de-ref (mostly for * code clarity) * * The general life time of a save point is: * . allocate a save point * . . start the save point, which sets the start idx and pos - and * pins the buffer pages with a use count * . . mark_end the save point, which sets the end idx and pos * . . . activate the save point, which sets the input streams pos * to the start * . . . deactivate the save point, which pops the save point stack * and restores the stream to its previous position * . . clear the save point. this clears its internal values and * releases its use counter on the buffer. As the buffers in use * count goes to zero it may release any unnecessary pages. * . free the save point * * this order of operation, as nested above, is enforced by checking * the state of the save points and member values like the end position. * * if the end position is not set it is treated as "to end of file" * */ final class UnifiedSavePointManagerX { private static final int FREE_LIST_LIMIT = 20; UnifiedInputStreamX _stream; UnifiedInputBufferX _buffer; SavePoint _inuse; SavePoint _free; int _free_count; SavePoint _active_stack; int _open_save_points; public UnifiedSavePointManagerX(UnifiedInputStreamX stream) { _stream = stream; _buffer = stream._buffer; _inuse = null; _free = null; _active_stack = null; } public final boolean isSavePointOpen() { return (_open_save_points > 0); } public final long lengthOf(SavePoint sp) { int start_idx = sp.getStartIdx(); int end_idx = sp.getEndIdx(); if (start_idx == -1 || end_idx == -1) { return 0; } long len; if (start_idx == end_idx) { // a very common case int start_pos = sp.getStartPos(); int end_pos = sp.getEndPos(); len = end_pos - start_pos; } else { UnifiedDataPageX start = _buffer.getPage(start_idx); UnifiedDataPageX end = _buffer.getPage(end_idx); long start_pos = start.getFilePosition(sp.getStartPos()); long end_pos = end.getFilePosition(sp.getEndPos()); len = end_pos - start_pos; } return len; } public final SavePoint savePointAllocate() { SavePoint sp; if (_free != null) { sp = _free; _free = sp._next; _free_count--; sp.clear(); } else { sp = new SavePoint(this); } sp._next = _inuse; sp._prev = null; if (_inuse != null) { _inuse._prev = sp; } else { _inuse = sp; } return sp; } public final void savePointFree(SavePoint sp) { assert(sp.isClear()); if (_free_count >= FREE_LIST_LIMIT) { // by not putting this on the free list // the GC is free to clean it up. return; } if (sp._prev == null) { sp._prev = sp._next; } else { _inuse = sp._next; } if (sp._next != null) { sp._next._prev = sp._prev; } sp._next = _free; _free = sp; _free_count++; } public final SavePoint savePointActiveTop() { return _active_stack; } public final void savePointPushActive(SavePoint sp, long line_number, long line_start) { assert(!sp.isActive()); int idx = _buffer.getCurrentPageIdx(); int pos = _stream._pos; int limit = _stream._limit; UnifiedDataPageX curr = _buffer.getPage(idx); // save our current state in the Save Point so when // we pop it off we can restore our current state sp.set_prev_pos(idx, pos, limit, line_number, line_start); // actually push this save point on the stack sp._next_active = _active_stack; _active_stack = sp; sp.set_active(); // if the start page is also the last page we // need set the limit to the sp end, otherwise // we use the limit from the page and we'll // deal with the last page when we get to it idx = sp.getStartIdx(); pos = sp.getStartPos(); curr = _buffer.getPage(idx); if (sp.getEndIdx() != sp.getStartIdx()) { limit = curr.getBufferLimit(); } else { limit = sp.getEndPos(); } _stream.make_page_current(curr, idx, pos, limit); } public final void savePointPopActive(SavePoint sp) { if (sp != _active_stack) { throw new IllegalArgumentException("save point being released isn't currently active"); } _active_stack = sp._next_active; sp._next_active = null; sp.set_inactive(); _stream.save_point_reset_to_prev(sp); return; } private void save_point_clear(SavePoint sp) { if (sp.isClear()) { return; } int start_idx = sp.getStartIdx(); int end_idx = sp.getEndIdx(); if (end_idx != -1 || start_idx != -1) { if (start_idx != -1) { _open_save_points--; save_point_unpin(sp); } } } private final void save_point_unpin(SavePoint sp) { if (sp.isActive()) { throw new IllegalArgumentException("you can't release an active save point"); } assert(sp.isDefined()); if (_buffer.decLock()) { if (_open_save_points == 0) { _buffer.resetToCurrentPage(); } } return; } private final SavePoint save_point_start(SavePoint sp, long line_number, long line_start) { if (sp.isDefined()) { throw new IllegalArgumentException("you can't start an active save point"); } int new_pinned_idx = _buffer.getCurrentPageIdx(); _buffer.incLock(); sp.set_start_pos(new_pinned_idx,_stream._pos, line_number, line_start); _open_save_points++; return sp; } private final void save_point_mark_end(SavePoint sp, int offset) { if (sp.isActive()) { throw new IllegalArgumentException("you can't start an active save point"); } UnifiedDataPageX curr = _buffer.getCurrentPage(); int curr_idx = _buffer.getCurrentPageIdx(); int curr_pos = _stream._pos + offset; // this adjusts the current page idx and pos (in the page buffer) // to handle the end point being offset from the current pos // since that may result in the end mark referencing a different page if (offset != 0) { if (curr_pos >= curr.getBufferLimit()) { curr_pos -= curr.getOriginalStartingOffset(); curr_idx++; curr = _buffer.getPage(curr_idx); } else if (curr_pos < curr.getStartingOffset()) { int pos_offset = curr_pos - curr.getOriginalStartingOffset(); curr_idx--; curr = _buffer.getPage(curr_idx); curr_pos = curr.getBufferLimit() - pos_offset; } if (curr == null || curr_pos >= curr.getBufferLimit() || curr_pos < curr.getStartingOffset()) { end_point_too_far(curr_idx); } } sp.set_end_pos(curr_idx, curr_pos); // we may be "re-setting" the idx (that's ok) return; } private final void end_point_too_far(int curr_idx) { String message = "end point [" + curr_idx + "] must be within 1 page of current [" + _buffer.getCurrentPageIdx() + "]"; throw new IllegalArgumentException(message); } public static class SavePoint { public enum SavePointState { CLEAR, DEFINED, ACTIVE } private UnifiedSavePointManagerX _owner; private SavePointState _state; private int _start_idx, _start_pos; private long _start_line_count; private long _start_line_start; private int _end_idx, _end_pos; private int _prev_idx, _prev_pos, _prev_limit; private long _prev_line_count; private long _prev_line_start; private SavePoint _next, _prev; private SavePoint _next_active; SavePoint(UnifiedSavePointManagerX owner) { clear(); _owner = owner; } private final void set_start_pos(int idx, int pos, long line_count, long line_start) { assert(_state == SavePointState.CLEAR); _state = SavePointState.DEFINED; _start_idx = idx; _start_pos = pos; _start_line_count = line_count; _start_line_start = line_start; } private final void set_end_pos(int idx, int pos) { assert(_state == SavePointState.DEFINED); _end_idx = idx; _end_pos = pos; } private final void set_prev_pos(int idx, int pos, int limit, long line_count, long line_start) { assert(_state == SavePointState.DEFINED); _prev_idx = idx; _prev_pos = pos; _prev_limit = limit; _prev_line_count = line_count; _prev_line_start = line_start; } public final void clear() { assert(_state != SavePointState.ACTIVE); if (isDefined()) { _owner.save_point_clear(this); } _state = SavePointState.CLEAR; _start_idx = -1; _end_idx = -1; _prev_idx = -1; } public final void start(long line_number, long line_start) { _owner.save_point_start(this, line_number, line_start); } public final void markEnd() { _owner.save_point_mark_end(this, 0); } public final void markEnd(int offset) { _owner.save_point_mark_end(this, offset); } public final void free() { _owner.savePointFree(this); } public final boolean isClear() { return (_state == SavePointState.CLEAR); } public final boolean isDefined() { return (_state == SavePointState.DEFINED || _state == SavePointState.ACTIVE); } public final boolean isActive() { return (_state == SavePointState.ACTIVE); } public final void set_active() { assert(_state == SavePointState.DEFINED); _state = SavePointState.ACTIVE; } public final void set_inactive() { assert(_state == SavePointState.ACTIVE); _state = SavePointState.DEFINED; } public final long length() { if (_start_idx == -1 || _end_idx == -1) { return 0; } return _owner.lengthOf(this); } public final int getStartIdx() { return _start_idx; } public final int getStartPos() { assert(_state != SavePointState.CLEAR); return _start_pos; } public final long getStartLineNumber() { return _start_line_count; } public final long getStartLineStart() { return _start_line_start; } public final long getStartFilePosition() { if (_start_idx == -1) return -1; UnifiedDataPageX p = _owner._buffer.getPage(_start_idx); return p.getFilePosition(_start_pos); } public final int getEndIdx() { return _end_idx; } public final int getEndPos() { assert(_state != SavePointState.CLEAR); return _end_pos; } public final long getEndFilePosition() { assert(_state != SavePointState.CLEAR); if (_end_idx == -1) return -1; UnifiedDataPageX p = _owner._buffer.getPage(_end_idx); return p.getFilePosition(_end_pos); } public final int getPrevIdx() { return _prev_idx; } public final int getPrevPos() { assert(_state != SavePointState.CLEAR); return _prev_pos; } public final int getPrevLimit() { assert(_state != SavePointState.CLEAR); return _prev_limit; } public final long getPrevLineNumber() { return _prev_line_count; } public final long getPrevLineStart() { return _prev_line_start; } } }