// 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.
// Author: sorenj@google.com (Jeffrey Sorensen)
#include <fst/symbol-table-ops.h>
namespace fst {
SymbolTable *MergeSymbolTable(const SymbolTable &left, const SymbolTable &right,
bool *right_relabel_output) {
// MergeSymbolTable detects several special cases. It will return a reference
// copied version of SymbolTable of left or right if either symbol table is
// a superset of the other.
SymbolTable *merged = new SymbolTable("merge_" + left.Name() + "_" +
right.Name());
// copy everything from the left symbol table
bool left_has_all = true, right_has_all = true, relabel = false;
SymbolTableIterator liter(left);
for (; !liter.Done(); liter.Next()) {
merged->AddSymbol(liter.Symbol(), liter.Value());
if (right_has_all) {
int64 key = right.Find(liter.Symbol());
if (key == -1) {
right_has_all = false;
} else if (!relabel && key != liter.Value()) {
relabel = true;
}
}
}
if (right_has_all) {
delete merged;
if (right_relabel_output != NULL) {
*right_relabel_output = relabel;
}
return right.Copy();
}
// add all symbols we can from right symbol table
vector<string> conflicts;
SymbolTableIterator riter(right);
for (; !riter.Done(); riter.Next()) {
int64 key = merged->Find(riter.Symbol());
if (key != -1) {
// Symbol already exists, maybe with different value
if (key != riter.Value()) {
relabel = true;
}
continue;
}
// Symbol doesn't exist from left
left_has_all = false;
if (!merged->Find(riter.Value()).empty()) {
// we can't add this where we want to, add it later, in order
conflicts.push_back(riter.Symbol());
continue;
}
// there is a hole and we can add this symbol with its id
merged->AddSymbol(riter.Symbol(), riter.Value());
}
if (right_relabel_output != NULL) {
*right_relabel_output = relabel;
}
if (left_has_all) {
delete merged;
return left.Copy();
}
// Add all symbols that conflicted, in order
for (int i= 0; i < conflicts.size(); ++i) {
merged->AddSymbol(conflicts[i]);
}
return merged;
}
SymbolTable *CompactSymbolTable(const SymbolTable &syms) {
map<int, string> sorted;
SymbolTableIterator stiter(syms);
for (; !stiter.Done(); stiter.Next()) {
sorted[stiter.Value()] = stiter.Symbol();
}
SymbolTable *compact = new SymbolTable(syms.Name() + "_compact");
uint64 newkey = 0;
for (map<int, string>::const_iterator si = sorted.begin();
si != sorted.end(); ++si) {
compact->AddSymbol(si->second, newkey++);
}
return compact;
}
SymbolTable *FstReadSymbols(const string &filename, bool input_symbols) {
ifstream in(filename.c_str(), ifstream::in | ifstream::binary);
if (!in) {
LOG(ERROR) << "FstReadSymbols: Can't open file " << filename;
return NULL;
}
FstHeader hdr;
if (!hdr.Read(in, filename)) {
LOG(ERROR) << "FstReadSymbols: Couldn't read header from " << filename;
return NULL;
}
if (hdr.GetFlags() & FstHeader::HAS_ISYMBOLS) {
SymbolTable *isymbols = SymbolTable::Read(in, filename);
if (isymbols == NULL) {
LOG(ERROR) << "FstReadSymbols: Could not read input symbols from "
<< filename;
return NULL;
}
if (input_symbols) {
return isymbols;
}
delete isymbols;
}
if (hdr.GetFlags() & FstHeader::HAS_OSYMBOLS) {
SymbolTable *osymbols = SymbolTable::Read(in, filename);
if (osymbols == NULL) {
LOG(ERROR) << "FstReadSymbols: Could not read output symbols from "
<< filename;
return NULL;
}
if (!input_symbols) {
return osymbols;
}
delete osymbols;
}
LOG(ERROR) << "FstReadSymbols: The file " << filename
<< " doesn't contain the requested symbols";
return NULL;
}
} // namespace fst