org.firebirdsql.gds.ClumpletReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaybird Show documentation
Show all versions of jaybird Show documentation
JDBC Driver for the Firebird RDBMS
/*
* 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 - 2025 Weber Informatics LLC | Privacy Policy