// Licensed 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. // // Copyright 2005-2010 Google, Inc. // All Rights Reserved. // // Author : Johan Schalkwyk // // \file // Classes to provide symbol-to-integer and integer-to-symbol mappings. #include #include DEFINE_bool(fst_compat_symbols, true, "Require symbol tables to match when appropriate"); DEFINE_string(fst_field_separator, "\t ", "Set of characters used as a separator between printed fields"); namespace fst { // Maximum line length in textual symbols file. const int kLineLen = 8096; // Identifies stream data as a symbol table (and its endianity) static const int32 kSymbolTableMagicNumber = 2125658996; SymbolTableTextOptions::SymbolTableTextOptions() : allow_negative(false), fst_field_separator(FLAGS_fst_field_separator) { } SymbolTableImpl* SymbolTableImpl::ReadText(istream &strm, const string &filename, const SymbolTableTextOptions &opts) { SymbolTableImpl* impl = new SymbolTableImpl(filename); int64 nline = 0; char line[kLineLen]; while (strm.getline(line, kLineLen)) { ++nline; vector col; string separator = opts.fst_field_separator + "\n"; SplitToVector(line, separator.c_str(), &col, true); if (col.size() == 0) // empty line continue; if (col.size() != 2) { LOG(ERROR) << "SymbolTable::ReadText: Bad number of columns (" << col.size() << "), " << "file = " << filename << ", line = " << nline << ":<" << line << ">"; delete impl; return 0; } const char *symbol = col[0]; const char *value = col[1]; char *p; int64 key = strtoll(value, &p, 10); if (p < value + strlen(value) || (!opts.allow_negative && key < 0) || key == -1) { LOG(ERROR) << "SymbolTable::ReadText: Bad non-negative integer \"" << value << "\", " << "file = " << filename << ", line = " << nline; delete impl; return 0; } impl->AddSymbol(symbol, key); } return impl; } void SymbolTableImpl::MaybeRecomputeCheckSum() const { { ReaderMutexLock check_sum_lock(&check_sum_mutex_); if (check_sum_finalized_) return; } // We'll aquire an exclusive lock to recompute the checksums. MutexLock check_sum_lock(&check_sum_mutex_); if (check_sum_finalized_) // Another thread (coming in around the same time return; // might have done it already). So we recheck. // Calculate the original label-agnostic check sum. CheckSummer check_sum; for (int64 i = 0; i < symbols_.size(); ++i) check_sum.Update(symbols_[i], strlen(symbols_[i]) + 1); check_sum_string_ = check_sum.Digest(); // Calculate the safer, label-dependent check sum. CheckSummer labeled_check_sum; for (int64 key = 0; key < dense_key_limit_; ++key) { ostringstream line; line << symbols_[key] << '\t' << key; labeled_check_sum.Update(line.str().data(), line.str().size()); } for (map::const_iterator it = key_map_.begin(); it != key_map_.end(); ++it) { if (it->first >= dense_key_limit_) { ostringstream line; line << it->second << '\t' << it->first; labeled_check_sum.Update(line.str().data(), line.str().size()); } } labeled_check_sum_string_ = labeled_check_sum.Digest(); check_sum_finalized_ = true; } int64 SymbolTableImpl::AddSymbol(const string& symbol, int64 key) { map::const_iterator it = symbol_map_.find(symbol.c_str()); if (it == symbol_map_.end()) { // only add if not in table check_sum_finalized_ = false; char *csymbol = new char[symbol.size() + 1]; strcpy(csymbol, symbol.c_str()); symbols_.push_back(csymbol); key_map_[key] = csymbol; symbol_map_[csymbol] = key; if (key >= available_key_) { available_key_ = key + 1; } } else { // Log if symbol already in table with different key if (it->second != key) { VLOG(1) << "SymbolTable::AddSymbol: symbol = " << symbol << " already in symbol_map_ with key = " << it->second << " but supplied new key = " << key << " (ignoring new key)"; } } return key; } static bool IsInRange(const vector >& ranges, int64 key) { if (ranges.size() == 0) return true; for (size_t i = 0; i < ranges.size(); ++i) { if (key >= ranges[i].first && key <= ranges[i].second) return true; } return false; } SymbolTableImpl* SymbolTableImpl::Read(istream &strm, const SymbolTableReadOptions& opts) { int32 magic_number = 0; ReadType(strm, &magic_number); if (!strm) { LOG(ERROR) << "SymbolTable::Read: read failed"; return 0; } string name; ReadType(strm, &name); SymbolTableImpl* impl = new SymbolTableImpl(name); ReadType(strm, &impl->available_key_); int64 size; ReadType(strm, &size); if (!strm) { LOG(ERROR) << "SymbolTable::Read: read failed"; delete impl; return 0; } string symbol; int64 key; impl->check_sum_finalized_ = false; for (size_t i = 0; i < size; ++i) { ReadType(strm, &symbol); ReadType(strm, &key); if (!strm) { LOG(ERROR) << "SymbolTable::Read: read failed"; delete impl; return 0; } char *csymbol = new char[symbol.size() + 1]; strcpy(csymbol, symbol.c_str()); impl->symbols_.push_back(csymbol); if (key == impl->dense_key_limit_ && key == impl->symbols_.size() - 1) impl->dense_key_limit_ = impl->symbols_.size(); else impl->key_map_[key] = csymbol; if (IsInRange(opts.string_hash_ranges, key)) { impl->symbol_map_[csymbol] = key; } } return impl; } bool SymbolTableImpl::Write(ostream &strm) const { WriteType(strm, kSymbolTableMagicNumber); WriteType(strm, name_); WriteType(strm, available_key_); int64 size = symbols_.size(); WriteType(strm, size); // first write out dense keys int64 i = 0; for (; i < dense_key_limit_; ++i) { WriteType(strm, string(symbols_[i])); WriteType(strm, i); } // next write out the remaining non densely packed keys for (map::const_iterator it = symbol_map_.begin(); it != symbol_map_.end(); ++it) { if ((it->second >= 0) && (it->second < dense_key_limit_)) continue; WriteType(strm, string(it->first)); WriteType(strm, it->second); ++i; } if (i != size) { LOG(ERROR) << "SymbolTable::Write: write failed"; return false; } strm.flush(); if (!strm) { LOG(ERROR) << "SymbolTable::Write: write failed"; return false; } return true; } const int64 SymbolTable::kNoSymbol; void SymbolTable::AddTable(const SymbolTable& table) { for (SymbolTableIterator iter(table); !iter.Done(); iter.Next()) impl_->AddSymbol(iter.Symbol()); } bool SymbolTable::WriteText(ostream &strm, const SymbolTableTextOptions &opts) const { if (opts.fst_field_separator.empty()) { LOG(ERROR) << "Missing required field separator"; return false; } bool once_only = false; for (SymbolTableIterator iter(*this); !iter.Done(); iter.Next()) { ostringstream line; if (iter.Value() < 0 && !opts.allow_negative && !once_only) { LOG(WARNING) << "Negative symbol table entry when not allowed"; once_only = true; } line << iter.Symbol() << opts.fst_field_separator[0] << iter.Value() << '\n'; strm.write(line.str().data(), line.str().length()); } return true; } } // namespace fst