/** * 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 * * https://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 #include "NodeImpl.hh" #include "ValidSchema.hh" #include "Validator.hh" namespace avro { Validator::Validator(ValidSchema schema) : schema_(std::move(schema)), nextType_(AVRO_NULL), expectedTypesFlag_(0), compoundStarted_(false), waitingForCount_(false), count_(0) { setupOperation(schema_.root()); } void Validator::setWaitingForCount() { waitingForCount_ = true; count_ = 0; expectedTypesFlag_ = typeToFlag(AVRO_INT) | typeToFlag(AVRO_LONG); nextType_ = AVRO_LONG; } void Validator::enumAdvance() { if (compoundStarted_) { setWaitingForCount(); compoundStarted_ = false; } else { waitingForCount_ = false; compoundStack_.pop_back(); } } bool Validator::countingSetup() { auto proceed = true; if (compoundStarted_) { setWaitingForCount(); compoundStarted_ = false; proceed = false; } else if (waitingForCount_) { waitingForCount_ = false; if (count_ == 0) { compoundStack_.pop_back(); proceed = false; } else { counters_.push_back(static_cast(count_)); } } return proceed; } void Validator::countingAdvance() { if (countingSetup()) { size_t index = (compoundStack_.back().pos)++; const NodePtr &node = compoundStack_.back().node; if (index < node->leaves()) { setupOperation(node->leafAt(index)); } else { compoundStack_.back().pos = 0; int count = --counters_.back(); if (count == 0) { counters_.pop_back(); compoundStarted_ = true; nextType_ = node->type(); expectedTypesFlag_ = typeToFlag(nextType_); } else { index = (compoundStack_.back().pos)++; setupOperation(node->leafAt(index)); } } } } void Validator::unionAdvance() { if (compoundStarted_) { setWaitingForCount(); compoundStarted_ = false; } else { waitingForCount_ = false; NodePtr node = compoundStack_.back().node; if (count_ < static_cast(node->leaves())) { compoundStack_.pop_back(); setupOperation(node->leafAt(static_cast(count_))); } else { throw Exception( boost::format("Union selection out of range, got %1%," " expecting 0-%2%") % count_ % (node->leaves() - 1)); } } } void Validator::fixedAdvance() { compoundStarted_ = false; compoundStack_.pop_back(); } int Validator::nextSizeExpected() const { return compoundStack_.back().node->fixedSize(); } void Validator::doAdvance() { using AdvanceFunc = void (Validator::*)(); // only the compound types need advance functions here static const AdvanceFunc funcs[] = { nullptr, // string nullptr, // bytes nullptr, // int nullptr, // long nullptr, // float nullptr, // double nullptr, // bool nullptr, // null &Validator::countingAdvance, // Record is treated like counting with count == 1 &Validator::enumAdvance, &Validator::countingAdvance, &Validator::countingAdvance, &Validator::unionAdvance, &Validator::fixedAdvance}; static_assert((sizeof(funcs) / sizeof(AdvanceFunc)) == (AVRO_NUM_TYPES), "Invalid number of advance functions"); expectedTypesFlag_ = 0; // loop until we encounter a next expected type, or we've exited all compound types while (!expectedTypesFlag_ && !compoundStack_.empty()) { Type type = compoundStack_.back().node->type(); AdvanceFunc func = funcs[type]; // only compound functions are put on the status stack so it is ok to // assume that func is not null assert(func); ((this)->*(func))(); } if (compoundStack_.empty()) { nextType_ = AVRO_NULL; } } void Validator::advance() { if (!waitingForCount_) { doAdvance(); } } void Validator::setCount(int64_t count) { if (!waitingForCount_) { throw Exception("Not expecting count"); } else if (count_ < 0) { throw Exception("Count cannot be negative"); } count_ = count; doAdvance(); } void Validator::setupFlag(Type type) { // use flags instead of strictly types, so that we can be more lax about the type // (for example, a long should be able to accept an int type, but not vice versa) static const flag_t flags[] = { typeToFlag(AVRO_STRING) | typeToFlag(AVRO_BYTES), typeToFlag(AVRO_STRING) | typeToFlag(AVRO_BYTES), typeToFlag(AVRO_INT), typeToFlag(AVRO_INT) | typeToFlag(AVRO_LONG), typeToFlag(AVRO_FLOAT), typeToFlag(AVRO_DOUBLE), typeToFlag(AVRO_BOOL), typeToFlag(AVRO_NULL), typeToFlag(AVRO_RECORD), typeToFlag(AVRO_ENUM), typeToFlag(AVRO_ARRAY), typeToFlag(AVRO_MAP), typeToFlag(AVRO_UNION), typeToFlag(AVRO_FIXED)}; static_assert((sizeof(flags) / sizeof(flag_t)) == (AVRO_NUM_TYPES), "Invalid number of avro type flags"); expectedTypesFlag_ = flags[type]; } void Validator::setupOperation(const NodePtr &node) { nextType_ = node->type(); if (nextType_ == AVRO_SYMBOLIC) { NodePtr actualNode = resolveSymbol(node); assert(actualNode); setupOperation(actualNode); return; } assert(nextType_ < AVRO_SYMBOLIC); setupFlag(nextType_); if (!isPrimitive(nextType_)) { compoundStack_.emplace_back(node); compoundStarted_ = true; } } bool Validator::getCurrentRecordName(std::string &name) const { auto found = false; name.clear(); // if the top of the stack is a record I want this record name auto idx = static_cast(compoundStack_.size() - ((!compoundStack_.empty() && (isPrimitive(nextType_) || nextType_ == AVRO_RECORD)) ? 1 : 2)); if (idx >= 0 && compoundStack_[idx].node->type() == AVRO_RECORD) { name = compoundStack_[idx].node->name().simpleName(); found = true; } return found; } bool Validator::getNextFieldName(std::string &name) const { auto found = false; name.clear(); auto idx = static_cast(compoundStack_.size() - (isCompound(nextType_) ? 2 : 1)); if (idx >= 0 && compoundStack_[idx].node->type() == AVRO_RECORD) { size_t pos = compoundStack_[idx].pos - 1; const NodePtr &node = compoundStack_[idx].node; if (pos < node->leaves()) { name = node->nameAt(pos); found = true; } } return found; } } // namespace avro