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

org.firebirdsql.gds.ClumpletReader Maven / Gradle / Ivy

There is a newer version: 4.0.10.java7
Show newest version
/*
 * Firebird Open Source JavaEE Connector - JDBC Driver
 *
 * Distributable under LGPL license.
 * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * LGPL License for more details.
 *
 * This file was created by members of the firebird development team.
 * All individual contributions remain the Copyright (C) of those
 * individuals.  Contributors to this file are either listed here or
 * can be obtained from a source control history command.
 *
 * All rights reserved.
 */
package org.firebirdsql.gds;

import org.firebirdsql.encodings.Encoding;
import org.firebirdsql.gds.ng.FbExceptionBuilder;

import java.nio.charset.Charset;
import java.sql.SQLException;
import java.util.Arrays;

import static org.firebirdsql.gds.ISCConstants.*;
import static org.firebirdsql.gds.JaybirdErrorCodes.jb_clumpletReaderUsageError;
import static org.firebirdsql.gds.JaybirdErrorCodes.jb_invalidClumpletStructure;

/**
 * Reader for clumplets, similar to the implementation {@code ClumpletReader.cpp}.
 *
 * @author Mark Rotteveel
 * @since 3.0
 */
public class ClumpletReader {

    private final Kind kind;
    private final byte[] buffer;
    private int spbState; // Reflects state of spb parser/writer
    private int position;

    public ClumpletReader(Kind kind, byte[] buffer) {
        this.kind = kind;
        this.buffer = buffer;
    }

    public boolean isTagged() {
        switch (kind) {
        case Tpb:
        case Tagged:
        case WideTagged:
        case SpbAttach:
            return true;
        }

        return false;
    }

    public int getBufferTag() throws SQLException {
        switch (kind) {
        case Tpb:
        case Tagged:
        case WideTagged:
            if (buffer.length == 0) {
                throw invalidStructure("empty buffer");
            }
            return buffer[0];
        case SpbStart:
        case UnTagged:
        case WideUnTagged:
        case SpbSendItems:
        case SpbReceiveItems:
            throw usageMistake("buffer is not tagged");
        case SpbAttach:
            if (buffer.length == 0) {
                throw invalidStructure("empty buffer");
            }
            switch (buffer[0]) {
            case isc_spb_version1:
                // This is old SPB format, it's almost like DPB -
                // buffer's tag is the first byte.
                return buffer[0];
            case isc_spb_version:
                // Buffer's tag is the second byte
                if (buffer.length == 1) {
                    throw invalidStructure("buffer too short (1 byte)");
                }
                return buffer[1];
            case isc_spb_version3:
                // This is wide SPB attach format - buffer's tag is the first byte.
                return buffer[0];
            default:
                throw invalidStructure("spb in service attach should begin with isc_spb_version1 or isc_spb_version");
            }
        default:
            throw new SQLException("Unexpected clumplet kind: " + kind);
        }
    }

    public ClumpletType getClumpletType(byte tag) throws SQLException {
        switch (kind) {
        case Tagged:
        case UnTagged:
        case SpbAttach:
            return ClumpletType.TraditionalDpb;
        case WideTagged:
        case WideUnTagged:
            return ClumpletType.Wide;
        case Tpb:
            switch (tag) {
            case isc_tpb_lock_write:
            case isc_tpb_lock_read:
            case isc_tpb_lock_timeout:
                return ClumpletType.TraditionalDpb;
            }
            return ClumpletType.SingleTpb;
        case SpbSendItems:
            switch (tag) {
            case isc_info_svc_auth_block:
                return ClumpletType.Wide;
            case isc_info_end:
            case isc_info_truncated:
            case isc_info_error:
            case isc_info_data_not_ready:
            case isc_info_length:
            case isc_info_flag_end:
                return ClumpletType.SingleTpb;
            }
            return ClumpletType.StringSpb;
        case SpbReceiveItems:
            return ClumpletType.SingleTpb;
        case SpbStart:
            switch (tag) {
            case isc_spb_auth_block:
            case isc_spb_trusted_auth:
            case isc_spb_auth_plugin_name:
            case isc_spb_auth_plugin_list:
                return ClumpletType.Wide;
            }
            switch (spbState) {
            case 0:
                return ClumpletType.SingleTpb;
            case isc_action_svc_backup:
            case isc_action_svc_restore:
                switch (tag) {
                case isc_spb_bkp_file:
                case isc_spb_dbname:
                case isc_spb_res_fix_fss_data:
                case isc_spb_res_fix_fss_metadata:
                case isc_spb_bkp_stat:
                    return ClumpletType.StringSpb;
                case isc_spb_bkp_factor:
                case isc_spb_bkp_length:
                case isc_spb_res_length:
                case isc_spb_res_buffers:
                case isc_spb_res_page_size:
                case isc_spb_options:
                case isc_spb_verbint:
                    return ClumpletType.IntSpb;
                case isc_spb_verbose:
                    return ClumpletType.SingleTpb;
                case isc_spb_res_access_mode:
                    return ClumpletType.ByteSpb;
                }
                throw invalidStructure("unknown parameter for backup/restore");
            case isc_action_svc_repair:
                switch (tag) {
                case isc_spb_dbname:
                    return ClumpletType.StringSpb;
                case isc_spb_options:
                case isc_spb_rpr_commit_trans:
                case isc_spb_rpr_rollback_trans:
                case isc_spb_rpr_recover_two_phase:
                    return ClumpletType.IntSpb;
                case isc_spb_rpr_commit_trans_64:
                case isc_spb_rpr_rollback_trans_64:
                case isc_spb_rpr_recover_two_phase_64:
                    return ClumpletType.BigIntSpb;
                }
                throw invalidStructure("unknown parameter for repair");
            case isc_action_svc_add_user:
            case isc_action_svc_delete_user:
            case isc_action_svc_modify_user:
            case isc_action_svc_display_user:
            case isc_action_svc_display_user_adm:
            case isc_action_svc_set_mapping:
            case isc_action_svc_drop_mapping:
                switch (tag) {
                case isc_spb_dbname:
                case isc_spb_sql_role_name:
                case isc_spb_sec_username:
                case isc_spb_sec_password:
                case isc_spb_sec_groupname:
                case isc_spb_sec_firstname:
                case isc_spb_sec_middlename:
                case isc_spb_sec_lastname:
                    return ClumpletType.StringSpb;
                case isc_spb_sec_userid:
                case isc_spb_sec_groupid:
                case isc_spb_sec_admin:
                    return ClumpletType.IntSpb;
                }
                throw invalidStructure("unknown parameter for security database operation");
            case isc_action_svc_properties:
                switch (tag) {
                case isc_spb_dbname:
                    return ClumpletType.StringSpb;
                case isc_spb_prp_page_buffers:
                case isc_spb_prp_sweep_interval:
                case isc_spb_prp_shutdown_db:
                case isc_spb_prp_deny_new_attachments:
                case isc_spb_prp_deny_new_transactions:
                case isc_spb_prp_set_sql_dialect:
                case isc_spb_options:
                case isc_spb_prp_force_shutdown:
                case isc_spb_prp_attachments_shutdown:
                case isc_spb_prp_transactions_shutdown:
                    return ClumpletType.IntSpb;
                case isc_spb_prp_reserve_space:
                case isc_spb_prp_write_mode:
                case isc_spb_prp_access_mode:
                case isc_spb_prp_shutdown_mode:
                case isc_spb_prp_online_mode:
                    return ClumpletType.ByteSpb;
                }
                throw invalidStructure("unknown parameter for setting database properties");
//		    case isc_action_svc_add_license:
//		    case isc_action_svc_remove_license:
            case isc_action_svc_db_stats:
                switch (tag) {
                case isc_spb_dbname:
                case isc_spb_command_line:
                case isc_spb_sts_table:
                    return ClumpletType.StringSpb;
                case isc_spb_options:
                    return ClumpletType.IntSpb;
                }
                throw invalidStructure("unknown parameter for getting statistics");
            case isc_action_svc_get_ib_log:
                throw invalidStructure("unknown parameter for getting log");
            case isc_action_svc_nbak:
            case isc_action_svc_nrest:
                switch (tag) {
                case isc_spb_nbk_file:
                case isc_spb_nbk_direct:
                case isc_spb_dbname:
                    return ClumpletType.StringSpb;
                case isc_spb_nbk_level:
                case isc_spb_options:
                    return ClumpletType.IntSpb;
                }
                throw invalidStructure("unknown parameter for nbackup");
            case isc_action_svc_trace_start:
            case isc_action_svc_trace_stop:
            case isc_action_svc_trace_suspend:
            case isc_action_svc_trace_resume:
                switch (tag) {
                case isc_spb_trc_cfg:
                case isc_spb_trc_name:
                    return ClumpletType.StringSpb;
                case isc_spb_trc_id:
                    return ClumpletType.IntSpb;
                }
                break;
            case isc_action_svc_validate:
                switch (tag) {
                case isc_spb_val_tab_incl:
                case isc_spb_val_tab_excl:
                case isc_spb_val_idx_incl:
                case isc_spb_val_idx_excl:
                case isc_spb_dbname:
                    return ClumpletType.StringSpb;
                case isc_spb_val_lock_timeout:
                    return ClumpletType.IntSpb;
                }
                break;
            }
            throw invalidStructure("wrong spb state");
        }
        throw invalidStructure("unknown reason");
    }

    public void adjustSpbState() throws SQLException {
        switch (kind) {
        case SpbStart:
            if (spbState == 0 &&                              // Just started with service start block ...
                    getClumpletSize(true, true, true) == 1) { // and this is action_XXX clumplet
                spbState = getClumpTag();
            }
            break;
        default:
            break;
        }
    }

    public int getClumpletSize(boolean wTag, boolean wLength, boolean wData) throws SQLException {
        // Check for EOF
        if (position >= buffer.length) {
            throw usageMistake("read past EOF");
        }

        int rc = wTag ? 1 : 0;
        int lengthSize = 0;
        int dataSize = 0;

        switch (getClumpletType(buffer[position])) {
        // This form allows clumplets of virtually any size
        case Wide:
            // Check did we receive length component for clumplet
            if (buffer.length - position < 5) {
                throw invalidStructure("buffer end before end of clumplet - no length component");
            }
            lengthSize = 4;
            dataSize = VaxEncoding.iscVaxInteger(buffer, position + 1, 4);
            break;

        // This is the most widely used form
        case TraditionalDpb:
            // Check did we receive length component for clumplet
            if (buffer.length - position < 2) {
                throw invalidStructure("buffer end before end of clumplet - no length component");
            }
            lengthSize = 1;
            dataSize = buffer[position + 1] & 0xFF;
            break;

        // Almost all TPB parameters are single bytes
        case SingleTpb:
            break;

        // Used in SPB for long strings
        case StringSpb:
            // Check did we receive length component for clumplet
            if (buffer.length - position < 3) {
                throw invalidStructure("buffer end before end of clumplet - no length component");
            }
            lengthSize = 2;
            dataSize = VaxEncoding.iscVaxInteger2(buffer, position + 1);
            break;

        // Used in SPB for 4-byte integers
        case IntSpb:
            dataSize = 4;
            break;

        // Used in SPB for 8-byte integers
        case BigIntSpb:
            dataSize = 8;
            break;

        // Used in SPB for single byte
        case ByteSpb:
            dataSize = 1;
            break;
        }

        int total = 1 + lengthSize + dataSize;
        if (position + total > buffer.length) {
            throw invalidStructure("buffer end before end of clumplet - clumplet too long");
        }

        if (wLength) {
            rc += lengthSize;
        }
        if (wData) {
            rc += dataSize;
        }
        return rc;
    }

    public void moveNext() throws SQLException {
        if (isEof()) {
            return; // no need to raise useless exceptions
        }
        int cs = getClumpletSize(true, true, true);
        adjustSpbState();
        position += cs;
    }

    public void rewind() throws SQLException {
        if (buffer == null || buffer.length == 0) {
            position = 0;
            spbState = 0;
            return;
        }
        switch (kind) {
        case UnTagged:
        case WideUnTagged:
        case SpbStart:
        case SpbSendItems:
        case SpbReceiveItems:
            position = 0;
            break;
        default:
            if (kind == Kind.SpbAttach && getBufferLength() > 0 && buffer[0] != isc_spb_version1) {
                position = 2;
            } else {
                position = 1;
            }
        }
        spbState = 0;
    }

    public boolean find(int tag) throws SQLException {
        final int markPosition = position;
        for (rewind(); !isEof(); moveNext()) {
            if (tag == getClumpTag()) {
                return true;
            }
        }
        position = markPosition;
        return false;
    }

    public boolean next(int tag) throws SQLException {
        if (!isEof()) {
            final int markPosition = position;
            if (tag == getClumpTag()) {
                moveNext();
            }
            for (; !isEof(); moveNext()) {
                if (tag == getClumpTag()) {
                    return true;
                }
            }
            position = markPosition;
        }
        return false;
    }

    // Methods which work with currently selected clumplet
    public int getClumpTag() throws SQLException {
        // Check for EOF
        if (position >= buffer.length) {
            throw usageMistake("read past EOF");
        }

        return buffer[position] & 0xFF;
    }

    public int getClumpLength() throws SQLException {
        return getClumpletSize(false, false, true);
    }

    public byte[] getBytes() throws SQLException {
        final int from = position + getClumpletSize(true, true, false);
        final int to = from + getClumpLength();
        return Arrays.copyOfRange(buffer, from, to);
    }

    public int getInt() throws SQLException {
        final int length = getClumpLength();
        if (length > 4) {
            throw invalidStructure("length of integer exceeds 4 bytes");
        }
        final int from = position + getClumpletSize(true, true, false);
        return VaxEncoding.iscVaxInteger(buffer, from, length);
    }

    public long getLong() throws SQLException {
        final int length = getClumpLength();
        if (length > 8) {
            throw invalidStructure("length of long exceeds 8 bytes");
        }
        final int from = position + getClumpletSize(true, true, false);
        return VaxEncoding.iscVaxLong(buffer, from, length);
    }

    public String getString(Encoding encoding) throws SQLException {
        final int length = getClumpLength();
        final int from = position + getClumpletSize(true, true, false);
        return encoding.decodeFromCharset(buffer, from, length);
    }

    public String getString(Charset charset) throws SQLException {
        final int length = getClumpLength();
        final int from = position + getClumpletSize(true, true, false);
        return new String(buffer, from, length, charset);
    }

    // TODO: Other types in ClumpletReader.cpp not copied

    public boolean isEof() {
        return position >= getBufferLength();
    }

    private int getBufferLength() {
        if (buffer.length == 1 && kind != Kind.UnTagged && kind != Kind.SpbStart &&
                kind != Kind.WideUnTagged && kind != Kind.SpbSendItems &&
                kind != Kind.SpbReceiveItems) {
            return 0;
        }
        return buffer.length;
    }

    private static SQLException invalidStructure(String message) {
        return FbExceptionBuilder.forException(jb_invalidClumpletStructure)
                .messageParameter(message)
                .toFlatSQLException();
    }

    private static SQLException usageMistake(String message) {
        return FbExceptionBuilder.forException(jb_clumpletReaderUsageError)
                .messageParameter(message)
                .toFlatSQLException();
    }

    public enum Kind {
        EndOfList,
        Tagged,
        UnTagged,
        SpbAttach,
        SpbStart,
        Tpb,
        WideTagged,
        WideUnTagged,
        SpbSendItems,
        SpbReceiveItems
    }

    public enum ClumpletType {
        TraditionalDpb,
        SingleTpb,
        StringSpb,
        IntSpb,
        BigIntSpb,
        ByteSpb,
        Wide
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy