| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License 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. |
| */ |
| |
| #include "decoder.hpp" |
| #include "logger.hpp" |
| #include "value.hpp" |
| |
| #define CHECK_REMAINING(SIZE, DETAIL) \ |
| do { \ |
| if (remaining_ < static_cast<size_t>(SIZE)) { \ |
| notify_error(DETAIL, SIZE); \ |
| return false; \ |
| } \ |
| } while (0) |
| |
| using namespace datastax::internal::core; |
| |
| void Decoder::maybe_log_remaining() const { |
| if (remaining_ > 0) { |
| LOG_TRACE("Data remaining in %s response: %u", type_, static_cast<unsigned int>(remaining_)); |
| } |
| } |
| |
| bool Decoder::decode_inet(Address* output) { |
| CHECK_REMAINING(sizeof(uint8_t), "length of inet"); |
| |
| uint8_t address_length = 0; |
| input_ = internal::decode_byte(input_, address_length); |
| remaining_ -= sizeof(uint8_t); |
| if (address_length > CASS_INET_V6_LENGTH) { |
| LOG_ERROR("Invalid inet address length of %d bytes", address_length); |
| return false; |
| } |
| |
| CHECK_REMAINING(address_length, "inet"); |
| uint8_t address[CASS_INET_V6_LENGTH]; |
| memcpy(address, input_, address_length); |
| input_ += address_length; |
| remaining_ -= address_length; |
| |
| CHECK_REMAINING(sizeof(int32_t), "port"); |
| int32_t port = 0; |
| input_ = internal::decode_int32(input_, port); |
| remaining_ -= sizeof(int32_t); |
| |
| *output = Address(address, address_length, port); |
| return output->is_valid_and_resolved(); |
| } |
| |
| bool Decoder::decode_inet(CassInet* output) { |
| CHECK_REMAINING(sizeof(uint8_t), "length of inet"); |
| |
| input_ = internal::decode_byte(input_, output->address_length); |
| remaining_ -= sizeof(uint8_t); |
| if (output->address_length > CASS_INET_V6_LENGTH) { |
| LOG_ERROR("Invalid inet address length of %d bytes", output->address_length); |
| return false; |
| } |
| |
| CHECK_REMAINING(output->address_length, "inet"); |
| memcpy(output->address, input_, output->address_length); |
| input_ += output->address_length; |
| remaining_ -= output->address_length; |
| return true; |
| } |
| |
| bool Decoder::as_inet(const int address_length, CassInet* output) const { |
| output->address_length = static_cast<uint8_t>(address_length); |
| if (output->address_length > CASS_INET_V6_LENGTH) { |
| LOG_ERROR("Invalid inet address length of %d bytes", output->address_length); |
| return false; |
| } |
| |
| CHECK_REMAINING(output->address_length, "inet"); |
| memcpy(output->address, input_, output->address_length); |
| return true; |
| } |
| |
| bool Decoder::decode_write_type(CassWriteType& output) { |
| StringRef write_type; |
| output = CASS_WRITE_TYPE_UNKNOWN; |
| if (!decode_string(&write_type)) return false; |
| |
| if (write_type == "SIMPLE") { |
| output = CASS_WRITE_TYPE_SIMPLE; |
| } else if (write_type == "BATCH") { |
| output = CASS_WRITE_TYPE_BATCH; |
| } else if (write_type == "UNLOGGED_BATCH") { |
| output = CASS_WRITE_TYPE_UNLOGGED_BATCH; |
| } else if (write_type == "COUNTER") { |
| output = CASS_WRITE_TYPE_COUNTER; |
| } else if (write_type == "BATCH_LOG") { |
| output = CASS_WRITE_TYPE_BATCH_LOG; |
| } else if (write_type == "CAS") { |
| output = CASS_WRITE_TYPE_CAS; |
| } else if (write_type == "VIEW") { |
| output = CASS_WRITE_TYPE_VIEW; |
| } else if (write_type == "CDC") { |
| output = CASS_WRITE_TYPE_CDC; |
| } else { |
| LOG_WARN("Invalid write type %.*s", (int)write_type.size(), write_type.data()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool Decoder::decode_warnings(WarningVec& output) { |
| if (remaining_ < sizeof(uint16_t)) { |
| notify_error("count of warnings", sizeof(uint16_t)); |
| return false; |
| } |
| uint16_t count = 0; |
| input_ = internal::decode_uint16(input_, count); |
| remaining_ -= sizeof(uint16_t); |
| |
| for (uint16_t i = 0; i < count; ++i) { |
| StringRef warning; |
| |
| if (!decode_string(&warning)) return false; |
| LOG_WARN("Server-side warning: %.*s", (int)warning.size(), warning.data()); |
| output.push_back(warning); |
| } |
| |
| return true; |
| } |
| |
| Value Decoder::decode_value(const DataType::ConstPtr& data_type) { |
| int32_t size = 0; |
| if (!decode_int32(size)) return Value(); |
| |
| if (size >= 0) { |
| Decoder decoder(input_, size, protocol_version_); |
| input_ += size; |
| remaining_ -= size; |
| |
| int32_t count = 0; |
| if (!data_type->is_collection()) { |
| return Value(data_type, decoder); |
| } else if (decoder.decode_int32(count)) { |
| return Value(data_type, count, decoder); |
| } |
| return Value(); |
| } |
| return Value(data_type); |
| } |
| |
| bool Decoder::update_value(Value& value) { |
| int32_t size = 0; |
| if (decode_int32(size)) { |
| if (size >= 0) { |
| Decoder decoder(input_, size, protocol_version_); |
| input_ += size; |
| remaining_ -= size; |
| return value.update(decoder); |
| } |
| Decoder decoder; |
| return value.update(decoder); |
| } |
| return false; |
| } |
| |
| void Decoder::notify_error(const char* detail, size_t bytes) const { |
| if (strlen(type_) == 0) { |
| LOG_ERROR("Expected at least %u byte%s to decode %s value", static_cast<unsigned int>(bytes), |
| (bytes > 1 ? "s" : ""), detail); |
| } else { |
| LOG_ERROR("Expected at least %u byte%s to decode %s %s response", |
| static_cast<unsigned int>(bytes), (bytes > 1 ? "s" : ""), detail, type_); |
| } |
| } |