All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.ion.impl.UnifiedSavePointManagerX Maven / Gradle / Ivy

There is a newer version: 1.5.1
Show newest version
/*
 * Copyright 2009-2016 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://aws.amazon.com/apache2.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 software.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;
        }
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy