Initial commit: FunASR Speech Recognition Toolkit
Update API Documentation / build-api-docs (push) Has been cancelled
Update API Documentation / build-api-docs (push) Has been cancelled
Add complete FunASR codebase including models, runtime, and documentation.
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
# Whenever make is run in this directory, call ./get_version.sh as the
|
||||
# first thing. This script regenereates ./version.h if necessary, e.g.
|
||||
# if it does not already exist or if the version number has changed.
|
||||
LOG := $(shell ./get_version.sh; echo " $$?")
|
||||
ifneq ($(strip $(LOG)), 0)
|
||||
RC := $(lastword $(LOG))
|
||||
OUT := $(wordlist 1,$(shell echo $$(($(words $(LOG))-1))),$(LOG))
|
||||
ifeq ($(RC),0)
|
||||
$(info $(OUT))
|
||||
else
|
||||
$(error $(OUT))
|
||||
endif
|
||||
endif
|
||||
|
||||
all:
|
||||
|
||||
include ../kaldi.mk
|
||||
|
||||
TESTFILES = kaldi-math-test io-funcs-test kaldi-error-test timer-test
|
||||
|
||||
OBJFILES = kaldi-math.o kaldi-error.o io-funcs.o kaldi-utils.o timer.o
|
||||
|
||||
LIBNAME = kaldi-base
|
||||
|
||||
ADDLIBS =
|
||||
|
||||
include ../makefiles/default_rules.mk
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2017 University of Southern California (Author: Dogan Can)
|
||||
|
||||
# See ../../COPYING for clarification regarding multiple authors
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
# WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
# MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
# See the Apache 2 License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
# Kaldi versioning is loosely based on the semantic versioning scheme. This
|
||||
# script tries to work out the version string from the partial version number
|
||||
# specified in src/.version along with the recent git history. By convention
|
||||
# src/.version specifies the first two components (MAJOR.MINOR) of the version
|
||||
# number. The third component (PATCH) is determined by counting how many
|
||||
# commits there are that are newer than than the last commit modifiying
|
||||
# src/.version. If there are uncommitted changes in the src/ directory, then
|
||||
# the version string is extended with a suffix (~N) specifiying the number of
|
||||
# files with uncommitted changes. The last component of the version string is
|
||||
# the abbreviated hash of the HEAD commit. If git history is not available or
|
||||
# if the file src/.short_version exists, then the version string defaults to
|
||||
# the number specified in src/.version.
|
||||
|
||||
set -e
|
||||
|
||||
# Change working directory to the directory where this script is located.
|
||||
cd "$(dirname ${BASH_SOURCE[0]})"
|
||||
|
||||
# Read the partial version number specified in the first line of src/.version.
|
||||
version=$(head -1 ../.version)
|
||||
|
||||
if [ -e ../.short_version ]; then
|
||||
echo "$0: File src/.short_version exists."
|
||||
echo "$0: Stopping the construction of full version number from git history."
|
||||
elif ! [[ $version =~ ^[0-9][0-9]*.[0-9][0-9]*$ ]]; then
|
||||
echo "$0: The version number \"$version\" specified in src/.version is not" \
|
||||
"in MAJOR.MINOR format."
|
||||
echo "$0: Stopping the construction of full version number from git history."
|
||||
elif ! which git >&/dev/null; then
|
||||
echo "$0: Git is not installed."
|
||||
echo "$0: Using the version number \"$version\" specified in src/.version."
|
||||
elif [ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" != true ]; then
|
||||
echo "$0: Git history is not available."
|
||||
echo "$0: Using the version number \"$version\" specified in src/.version."
|
||||
else
|
||||
# Figure out patch number.
|
||||
version_commit=$(git log -1 --pretty=oneline ../.version | awk '{print $1}')
|
||||
patch_number=$(git rev-list ${version_commit}..HEAD | wc -l | awk '{print $1}')
|
||||
version="$version.$patch_number"
|
||||
|
||||
# Check for uncommitted changes in src/.
|
||||
uncommitted_changes=$(git diff-index HEAD -- .. | wc -l | awk '{print $1}')
|
||||
if [ $uncommitted_changes -gt 0 ]; then
|
||||
# Add suffix ~N if there are N files in src/ with uncommitted changes
|
||||
version="$version~$uncommitted_changes"
|
||||
fi
|
||||
|
||||
# Figure out HEAD commit SHA-1.
|
||||
head_commit=$(git log -1 --pretty=oneline | awk '{print $1}')
|
||||
head_commit_short=$(git log -1 --oneline --abbrev=4 | awk '{print $1}')
|
||||
version="$version-${head_commit_short}"
|
||||
fi
|
||||
|
||||
# Empty version number is not allowed.
|
||||
if [ -z "$version" ]; then
|
||||
version="?"
|
||||
fi
|
||||
|
||||
# Write version info to a temporary file.
|
||||
temp=$(mktemp /tmp/temp.XXXXXX)
|
||||
trap 'rm -f "$temp"' EXIT
|
||||
echo "// This file was automatically created by ./get_version.sh." > $temp
|
||||
echo "// It is only included by ./kaldi-error.cc." >> $temp
|
||||
echo "#define KALDI_VERSION \"$version\"" >> $temp
|
||||
if [ -n "$head_commit" ]; then
|
||||
echo "#define KALDI_GIT_HEAD \"$head_commit\"" >> $temp
|
||||
fi
|
||||
|
||||
# Overwrite ./version.h with the temporary file if they are different.
|
||||
if ! cmp -s $temp version.h; then
|
||||
cp $temp version.h
|
||||
chmod 644 version.h
|
||||
fi
|
||||
exit 0
|
||||
@@ -0,0 +1,327 @@
|
||||
// base/io-funcs-inl.h
|
||||
|
||||
// Copyright 2009-2011 Microsoft Corporation; Saarland University;
|
||||
// Jan Silovsky; Yanmin Qian;
|
||||
// Johns Hopkins University (Author: Daniel Povey)
|
||||
// 2016 Xiaohui Zhang
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef KALDI_BASE_IO_FUNCS_INL_H_
|
||||
#define KALDI_BASE_IO_FUNCS_INL_H_ 1
|
||||
|
||||
// Do not include this file directly. It is included by base/io-funcs.h
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
// Template that covers integers.
|
||||
template<class T> void WriteBasicType(std::ostream &os,
|
||||
bool binary, T t) {
|
||||
// Compile time assertion that this is not called with a wrong type.
|
||||
KALDI_ASSERT_IS_INTEGER_TYPE(T);
|
||||
if (binary) {
|
||||
char len_c = (std::numeric_limits<T>::is_signed ? 1 : -1)
|
||||
* static_cast<char>(sizeof(t));
|
||||
os.put(len_c);
|
||||
os.write(reinterpret_cast<const char *>(&t), sizeof(t));
|
||||
} else {
|
||||
if (sizeof(t) == 1)
|
||||
os << static_cast<int16>(t) << " ";
|
||||
else
|
||||
os << t << " ";
|
||||
}
|
||||
if (os.fail()) {
|
||||
KALDI_ERR << "Write failure in WriteBasicType.";
|
||||
}
|
||||
}
|
||||
|
||||
// Template that covers integers.
|
||||
template<class T> inline void ReadBasicType(std::istream &is,
|
||||
bool binary, T *t) {
|
||||
KALDI_PARANOID_ASSERT(t != NULL);
|
||||
// Compile time assertion that this is not called with a wrong type.
|
||||
KALDI_ASSERT_IS_INTEGER_TYPE(T);
|
||||
if (binary) {
|
||||
int len_c_in = is.get();
|
||||
if (len_c_in == -1)
|
||||
KALDI_ERR << "ReadBasicType: encountered end of stream.";
|
||||
char len_c = static_cast<char>(len_c_in), len_c_expected
|
||||
= (std::numeric_limits<T>::is_signed ? 1 : -1)
|
||||
* static_cast<char>(sizeof(*t));
|
||||
if (len_c != len_c_expected) {
|
||||
KALDI_ERR << "ReadBasicType: did not get expected integer type, "
|
||||
<< static_cast<int>(len_c)
|
||||
<< " vs. " << static_cast<int>(len_c_expected)
|
||||
<< ". You can change this code to successfully"
|
||||
<< " read it later, if needed.";
|
||||
// insert code here to read "wrong" type. Might have a switch statement.
|
||||
}
|
||||
is.read(reinterpret_cast<char *>(t), sizeof(*t));
|
||||
} else {
|
||||
if (sizeof(*t) == 1) {
|
||||
int16 i;
|
||||
is >> i;
|
||||
*t = i;
|
||||
} else {
|
||||
is >> *t;
|
||||
}
|
||||
}
|
||||
if (is.fail()) {
|
||||
KALDI_ERR << "Read failure in ReadBasicType, file position is "
|
||||
<< is.tellg() << ", next char is " << is.peek();
|
||||
}
|
||||
}
|
||||
|
||||
// Template that covers integers.
|
||||
template<class T>
|
||||
inline void WriteIntegerPairVector(std::ostream &os, bool binary,
|
||||
const std::vector<std::pair<T, T> > &v) {
|
||||
// Compile time assertion that this is not called with a wrong type.
|
||||
KALDI_ASSERT_IS_INTEGER_TYPE(T);
|
||||
if (binary) {
|
||||
char sz = sizeof(T); // this is currently just a check.
|
||||
os.write(&sz, 1);
|
||||
int32 vecsz = static_cast<int32>(v.size());
|
||||
KALDI_ASSERT((size_t)vecsz == v.size());
|
||||
os.write(reinterpret_cast<const char *>(&vecsz), sizeof(vecsz));
|
||||
if (vecsz != 0) {
|
||||
os.write(reinterpret_cast<const char *>(&(v[0])), sizeof(T) * vecsz * 2);
|
||||
}
|
||||
} else {
|
||||
// focus here is on prettiness of text form rather than
|
||||
// efficiency of reading-in.
|
||||
// reading-in is dominated by low-level operations anyway:
|
||||
// for efficiency use binary.
|
||||
os << "[ ";
|
||||
typename std::vector<std::pair<T, T> >::const_iterator iter = v.begin(),
|
||||
end = v.end();
|
||||
for (; iter != end; ++iter) {
|
||||
if (sizeof(T) == 1)
|
||||
os << static_cast<int16>(iter->first) << ','
|
||||
<< static_cast<int16>(iter->second) << ' ';
|
||||
else
|
||||
os << iter->first << ','
|
||||
<< iter->second << ' ';
|
||||
}
|
||||
os << "]\n";
|
||||
}
|
||||
if (os.fail()) {
|
||||
KALDI_ERR << "Write failure in WriteIntegerPairVector.";
|
||||
}
|
||||
}
|
||||
|
||||
// Template that covers integers.
|
||||
template<class T>
|
||||
inline void ReadIntegerPairVector(std::istream &is, bool binary,
|
||||
std::vector<std::pair<T, T> > *v) {
|
||||
KALDI_ASSERT_IS_INTEGER_TYPE(T);
|
||||
KALDI_ASSERT(v != NULL);
|
||||
if (binary) {
|
||||
int sz = is.peek();
|
||||
if (sz == sizeof(T)) {
|
||||
is.get();
|
||||
} else { // this is currently just a check.
|
||||
KALDI_ERR << "ReadIntegerPairVector: expected to see type of size "
|
||||
<< sizeof(T) << ", saw instead " << sz << ", at file position "
|
||||
<< is.tellg();
|
||||
}
|
||||
int32 vecsz;
|
||||
is.read(reinterpret_cast<char *>(&vecsz), sizeof(vecsz));
|
||||
if (is.fail() || vecsz < 0) goto bad;
|
||||
v->resize(vecsz);
|
||||
if (vecsz > 0) {
|
||||
is.read(reinterpret_cast<char *>(&((*v)[0])), sizeof(T)*vecsz*2);
|
||||
}
|
||||
} else {
|
||||
std::vector<std::pair<T, T> > tmp_v; // use temporary so v doesn't use extra memory
|
||||
// due to resizing.
|
||||
is >> std::ws;
|
||||
if (is.peek() != static_cast<int>('[')) {
|
||||
KALDI_ERR << "ReadIntegerPairVector: expected to see [, saw "
|
||||
<< is.peek() << ", at file position " << is.tellg();
|
||||
}
|
||||
is.get(); // consume the '['.
|
||||
is >> std::ws; // consume whitespace.
|
||||
while (is.peek() != static_cast<int>(']')) {
|
||||
if (sizeof(T) == 1) { // read/write chars as numbers.
|
||||
int16 next_t1, next_t2;
|
||||
is >> next_t1;
|
||||
if (is.fail()) goto bad;
|
||||
if (is.peek() != static_cast<int>(','))
|
||||
KALDI_ERR << "ReadIntegerPairVector: expected to see ',', saw "
|
||||
<< is.peek() << ", at file position " << is.tellg();
|
||||
is.get(); // consume the ','.
|
||||
is >> next_t2 >> std::ws;
|
||||
if (is.fail()) goto bad;
|
||||
else
|
||||
tmp_v.push_back(std::make_pair<T, T>((T)next_t1, (T)next_t2));
|
||||
} else {
|
||||
T next_t1, next_t2;
|
||||
is >> next_t1;
|
||||
if (is.fail()) goto bad;
|
||||
if (is.peek() != static_cast<int>(','))
|
||||
KALDI_ERR << "ReadIntegerPairVector: expected to see ',', saw "
|
||||
<< is.peek() << ", at file position " << is.tellg();
|
||||
is.get(); // consume the ','.
|
||||
is >> next_t2 >> std::ws;
|
||||
if (is.fail()) goto bad;
|
||||
else
|
||||
tmp_v.push_back(std::pair<T, T>(next_t1, next_t2));
|
||||
}
|
||||
}
|
||||
is.get(); // get the final ']'.
|
||||
*v = tmp_v; // could use std::swap to use less temporary memory, but this
|
||||
// uses less permanent memory.
|
||||
}
|
||||
if (!is.fail()) return;
|
||||
bad:
|
||||
KALDI_ERR << "ReadIntegerPairVector: read failure at file position "
|
||||
<< is.tellg();
|
||||
}
|
||||
|
||||
template<class T> inline void WriteIntegerVector(std::ostream &os, bool binary,
|
||||
const std::vector<T> &v) {
|
||||
// Compile time assertion that this is not called with a wrong type.
|
||||
KALDI_ASSERT_IS_INTEGER_TYPE(T);
|
||||
if (binary) {
|
||||
char sz = sizeof(T); // this is currently just a check.
|
||||
os.write(&sz, 1);
|
||||
int32 vecsz = static_cast<int32>(v.size());
|
||||
KALDI_ASSERT((size_t)vecsz == v.size());
|
||||
os.write(reinterpret_cast<const char *>(&vecsz), sizeof(vecsz));
|
||||
if (vecsz != 0) {
|
||||
os.write(reinterpret_cast<const char *>(&(v[0])), sizeof(T)*vecsz);
|
||||
}
|
||||
} else {
|
||||
// focus here is on prettiness of text form rather than
|
||||
// efficiency of reading-in.
|
||||
// reading-in is dominated by low-level operations anyway:
|
||||
// for efficiency use binary.
|
||||
os << "[ ";
|
||||
typename std::vector<T>::const_iterator iter = v.begin(), end = v.end();
|
||||
for (; iter != end; ++iter) {
|
||||
if (sizeof(T) == 1)
|
||||
os << static_cast<int16>(*iter) << " ";
|
||||
else
|
||||
os << *iter << " ";
|
||||
}
|
||||
os << "]\n";
|
||||
}
|
||||
if (os.fail()) {
|
||||
KALDI_ERR << "Write failure in WriteIntegerVector.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class T> inline void ReadIntegerVector(std::istream &is,
|
||||
bool binary,
|
||||
std::vector<T> *v) {
|
||||
KALDI_ASSERT_IS_INTEGER_TYPE(T);
|
||||
KALDI_ASSERT(v != NULL);
|
||||
if (binary) {
|
||||
int sz = is.peek();
|
||||
if (sz == sizeof(T)) {
|
||||
is.get();
|
||||
} else { // this is currently just a check.
|
||||
KALDI_ERR << "ReadIntegerVector: expected to see type of size "
|
||||
<< sizeof(T) << ", saw instead " << sz << ", at file position "
|
||||
<< is.tellg();
|
||||
}
|
||||
int32 vecsz;
|
||||
is.read(reinterpret_cast<char *>(&vecsz), sizeof(vecsz));
|
||||
if (is.fail() || vecsz < 0) goto bad;
|
||||
v->resize(vecsz);
|
||||
if (vecsz > 0) {
|
||||
is.read(reinterpret_cast<char *>(&((*v)[0])), sizeof(T)*vecsz);
|
||||
}
|
||||
} else {
|
||||
std::vector<T> tmp_v; // use temporary so v doesn't use extra memory
|
||||
// due to resizing.
|
||||
is >> std::ws;
|
||||
if (is.peek() != static_cast<int>('[')) {
|
||||
KALDI_ERR << "ReadIntegerVector: expected to see [, saw "
|
||||
<< is.peek() << ", at file position " << is.tellg();
|
||||
}
|
||||
is.get(); // consume the '['.
|
||||
is >> std::ws; // consume whitespace.
|
||||
while (is.peek() != static_cast<int>(']')) {
|
||||
if (sizeof(T) == 1) { // read/write chars as numbers.
|
||||
int16 next_t;
|
||||
is >> next_t >> std::ws;
|
||||
if (is.fail()) goto bad;
|
||||
else
|
||||
tmp_v.push_back((T)next_t);
|
||||
} else {
|
||||
T next_t;
|
||||
is >> next_t >> std::ws;
|
||||
if (is.fail()) goto bad;
|
||||
else
|
||||
tmp_v.push_back(next_t);
|
||||
}
|
||||
}
|
||||
is.get(); // get the final ']'.
|
||||
*v = tmp_v; // could use std::swap to use less temporary memory, but this
|
||||
// uses less permanent memory.
|
||||
}
|
||||
if (!is.fail()) return;
|
||||
bad:
|
||||
KALDI_ERR << "ReadIntegerVector: read failure at file position "
|
||||
<< is.tellg();
|
||||
}
|
||||
|
||||
|
||||
// Initialize an opened stream for writing by writing an optional binary
|
||||
// header and modifying the floating-point precision.
|
||||
inline void InitKaldiOutputStream(std::ostream &os, bool binary) {
|
||||
// This does not throw exceptions (does not check for errors).
|
||||
if (binary) {
|
||||
os.put('\0');
|
||||
os.put('B');
|
||||
}
|
||||
// Note, in non-binary mode we may at some point want to mess with
|
||||
// the precision a bit.
|
||||
// 7 is a bit more than the precision of float..
|
||||
if (os.precision() < 7)
|
||||
os.precision(7);
|
||||
}
|
||||
|
||||
/// Initialize an opened stream for reading by detecting the binary header and
|
||||
// setting the "binary" value appropriately.
|
||||
inline bool InitKaldiInputStream(std::istream &is, bool *binary) {
|
||||
// Sets the 'binary' variable.
|
||||
// Throws exception in the very unusual situation that stream
|
||||
// starts with '\0' but not then 'B'.
|
||||
|
||||
if (is.peek() == '\0') { // seems to be binary
|
||||
is.get();
|
||||
if (is.peek() != 'B') {
|
||||
return false;
|
||||
}
|
||||
is.get();
|
||||
*binary = true;
|
||||
return true;
|
||||
} else {
|
||||
*binary = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace kaldi.
|
||||
|
||||
#endif // KALDI_BASE_IO_FUNCS_INL_H_
|
||||
@@ -0,0 +1,160 @@
|
||||
// base/io-funcs-test.cc
|
||||
|
||||
// Copyright 2009-2011 Microsoft Corporation
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include "base/io-funcs.h"
|
||||
#include "base/kaldi-math.h"
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
void UnitTestIo(bool binary) {
|
||||
{
|
||||
const char *filename = "tmpf";
|
||||
std::ofstream outfile(filename, std::ios_base::out | std::ios_base::binary);
|
||||
InitKaldiOutputStream(outfile, binary);
|
||||
if (!binary) outfile << "\t";
|
||||
int64 i1 = Rand() % 10000;
|
||||
WriteBasicType(outfile, binary, i1);
|
||||
uint16 i2 = Rand() % 10000;
|
||||
WriteBasicType(outfile, binary, i2);
|
||||
if (!binary) outfile << "\t";
|
||||
char c = Rand();
|
||||
WriteBasicType(outfile, binary, c);
|
||||
if (!binary && Rand()%2 == 0) outfile << " \n";
|
||||
std::vector<int32> vec1;
|
||||
WriteIntegerVector(outfile, binary, vec1);
|
||||
if (!binary && Rand()%2 == 0) outfile << " \n";
|
||||
std::vector<uint16> vec2;
|
||||
for (size_t i = 0; i < 10; i++) vec2.push_back(Rand()%100 - 10);
|
||||
WriteIntegerVector(outfile, binary, vec2);
|
||||
if (!binary) outfile << " \n";
|
||||
std::vector<char> vec3;
|
||||
|
||||
int32 size = RandInt(0, 10);
|
||||
for (size_t i = 0; i < size; i++) vec3.push_back(Rand()%100);
|
||||
WriteIntegerVector(outfile, binary, vec3);
|
||||
std::vector<std::pair<int32, int32> > vec4;
|
||||
WriteIntegerPairVector(outfile, binary, vec4);
|
||||
if (!binary && Rand()%2 == 0) outfile << " \n";
|
||||
std::vector<std::pair<uint16, uint16> > vec5;
|
||||
for (size_t i = 0; i < size; i++) vec5.push_back(std::make_pair<uint16, uint16>(Rand()%100 - 10, Rand()%100 - 10));
|
||||
WriteIntegerPairVector(outfile, binary, vec5);
|
||||
if (!binary) outfile << " \n";
|
||||
std::vector<std::pair<char, char> > vec6;
|
||||
for (size_t i = 0; i < size; i++) vec6.push_back(std::make_pair<char, char>(Rand()%100, Rand()%100));
|
||||
WriteIntegerPairVector(outfile, binary, vec6);
|
||||
if (!binary && Rand()%2 == 0) outfile << " \n";
|
||||
const char *token1 = "Hi";
|
||||
WriteToken(outfile, binary, token1);
|
||||
if (!binary) outfile << " \n";
|
||||
std::string token2 = "There.";
|
||||
WriteToken(outfile, binary, token2);
|
||||
if (!binary && Rand()%2 == 0) outfile << " \n";
|
||||
std::string token3 = "You.";
|
||||
WriteToken(outfile, binary, token3);
|
||||
if (!binary && Rand()%2 == 0) outfile << " ";
|
||||
float f1 = RandUniform();
|
||||
WriteBasicType(outfile, binary, f1);
|
||||
if (!binary && Rand()%2 == 0) outfile << "\t";
|
||||
float f2 = RandUniform();
|
||||
WriteBasicType(outfile, binary, f2);
|
||||
double d1 = RandUniform();
|
||||
WriteBasicType(outfile, binary, d1);
|
||||
if (!binary && Rand()%2 == 0) outfile << "\t";
|
||||
double d2 = RandUniform();
|
||||
WriteBasicType(outfile, binary, d2);
|
||||
if (!binary && Rand()%2 == 0) outfile << "\t";
|
||||
outfile.close();
|
||||
|
||||
{
|
||||
std::ifstream infile(filename, std::ios_base::in | std::ios_base::binary);
|
||||
bool binary_in;
|
||||
InitKaldiInputStream(infile, &binary_in);
|
||||
int64 i1_in;
|
||||
ReadBasicType(infile, binary_in, &i1_in);
|
||||
KALDI_ASSERT(i1_in == i1);
|
||||
uint16 i2_in;
|
||||
ReadBasicType(infile, binary_in, &i2_in);
|
||||
KALDI_ASSERT(i2_in == i2);
|
||||
char c_in;
|
||||
ReadBasicType(infile, binary_in, &c_in);
|
||||
KALDI_ASSERT(c_in == c);
|
||||
std::vector<int32> vec1_in;
|
||||
ReadIntegerVector(infile, binary_in, &vec1_in);
|
||||
KALDI_ASSERT(vec1_in == vec1);
|
||||
std::vector<uint16> vec2_in;
|
||||
ReadIntegerVector(infile, binary_in, &vec2_in);
|
||||
KALDI_ASSERT(vec2_in == vec2);
|
||||
std::vector<char> vec3_in;
|
||||
ReadIntegerVector(infile, binary_in, &vec3_in);
|
||||
KALDI_ASSERT(vec3_in == vec3);
|
||||
std::vector<std::pair<int32, int32> > vec4_in;
|
||||
ReadIntegerPairVector(infile, binary_in, &vec4_in);
|
||||
KALDI_ASSERT(vec4_in == vec4);
|
||||
std::vector<std::pair<uint16, uint16> > vec5_in;
|
||||
ReadIntegerPairVector(infile, binary_in, &vec5_in);
|
||||
KALDI_ASSERT(vec5_in == vec5);
|
||||
std::vector<std::pair<char, char> > vec6_in;
|
||||
ReadIntegerPairVector(infile, binary_in, &vec6_in);
|
||||
KALDI_ASSERT(vec6_in == vec6);
|
||||
std::string token1_in, token2_in;
|
||||
KALDI_ASSERT(Peek(infile, binary_in) == static_cast<int>(*token1));
|
||||
KALDI_ASSERT(PeekToken(infile, binary_in) == static_cast<int>(*token1));
|
||||
// Note:
|
||||
// the stuff with skipping over '<' is tested in ../util/kaldi-io-test.cc,
|
||||
// since we need to make sure it works with pipes.
|
||||
ReadToken(infile, binary_in, &token1_in);
|
||||
KALDI_ASSERT(token1_in == std::string(token1));
|
||||
ReadToken(infile, binary_in, &token2_in);
|
||||
KALDI_ASSERT(token2_in == std::string(token2));
|
||||
if (Rand() % 2 == 0)
|
||||
ExpectToken(infile, binary_in, token3.c_str());
|
||||
else
|
||||
ExpectToken(infile, binary_in, token3);
|
||||
float f1_in; // same type.
|
||||
ReadBasicType(infile, binary_in, &f1_in);
|
||||
AssertEqual(f1_in, f1);
|
||||
double f2_in; // wrong type.
|
||||
ReadBasicType(infile, binary_in, &f2_in);
|
||||
AssertEqual(f2_in, f2);
|
||||
double d1_in; // same type.
|
||||
ReadBasicType(infile, binary_in, &d1_in);
|
||||
AssertEqual(d1_in, d1);
|
||||
float d2_in; // wrong type.
|
||||
ReadBasicType(infile, binary_in, &d2_in);
|
||||
AssertEqual(d2_in, d2);
|
||||
KALDI_ASSERT(Peek(infile, binary_in) == -1);
|
||||
KALDI_ASSERT(PeekToken(infile, binary_in) == -1);
|
||||
}
|
||||
unlink(filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // end namespace kaldi.
|
||||
|
||||
int main() {
|
||||
using namespace kaldi;
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
UnitTestIo(false);
|
||||
UnitTestIo(true);
|
||||
}
|
||||
KALDI_ASSERT(1); // just to check that KALDI_ASSERT does not fail for 1.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
// base/io-funcs.cc
|
||||
|
||||
// Copyright 2009-2011 Microsoft Corporation; Saarland University
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "base/io-funcs.h"
|
||||
#include "base/kaldi-math.h"
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
template<>
|
||||
void WriteBasicType<bool>(std::ostream &os, bool binary, bool b) {
|
||||
os << (b ? "T":"F");
|
||||
if (!binary) os << " ";
|
||||
if (os.fail())
|
||||
KALDI_ERR << "Write failure in WriteBasicType<bool>";
|
||||
}
|
||||
|
||||
template<>
|
||||
void ReadBasicType<bool>(std::istream &is, bool binary, bool *b) {
|
||||
KALDI_PARANOID_ASSERT(b != NULL);
|
||||
if (!binary) is >> std::ws; // eat up whitespace.
|
||||
char c = is.peek();
|
||||
if (c == 'T') {
|
||||
*b = true;
|
||||
is.get();
|
||||
} else if (c == 'F') {
|
||||
*b = false;
|
||||
is.get();
|
||||
} else {
|
||||
KALDI_ERR << "Read failure in ReadBasicType<bool>, file position is "
|
||||
<< is.tellg() << ", next char is " << CharToString(c);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void WriteBasicType<float>(std::ostream &os, bool binary, float f) {
|
||||
if (binary) {
|
||||
char c = sizeof(f);
|
||||
os.put(c);
|
||||
os.write(reinterpret_cast<const char *>(&f), sizeof(f));
|
||||
} else {
|
||||
os << f << " ";
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void WriteBasicType<double>(std::ostream &os, bool binary, double f) {
|
||||
if (binary) {
|
||||
char c = sizeof(f);
|
||||
os.put(c);
|
||||
os.write(reinterpret_cast<const char *>(&f), sizeof(f));
|
||||
} else {
|
||||
os << f << " ";
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void ReadBasicType<float>(std::istream &is, bool binary, float *f) {
|
||||
KALDI_PARANOID_ASSERT(f != NULL);
|
||||
if (binary) {
|
||||
double d;
|
||||
int c = is.peek();
|
||||
if (c == sizeof(*f)) {
|
||||
is.get();
|
||||
is.read(reinterpret_cast<char*>(f), sizeof(*f));
|
||||
} else if (c == sizeof(d)) {
|
||||
ReadBasicType(is, binary, &d);
|
||||
*f = d;
|
||||
} else {
|
||||
KALDI_ERR << "ReadBasicType: expected float, saw " << is.peek()
|
||||
<< ", at file position " << is.tellg();
|
||||
}
|
||||
} else {
|
||||
is >> *f;
|
||||
}
|
||||
if (is.fail()) {
|
||||
KALDI_ERR << "ReadBasicType: failed to read, at file position "
|
||||
<< is.tellg();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void ReadBasicType<double>(std::istream &is, bool binary, double *d) {
|
||||
KALDI_PARANOID_ASSERT(d != NULL);
|
||||
if (binary) {
|
||||
float f;
|
||||
int c = is.peek();
|
||||
if (c == sizeof(*d)) {
|
||||
is.get();
|
||||
is.read(reinterpret_cast<char*>(d), sizeof(*d));
|
||||
} else if (c == sizeof(f)) {
|
||||
ReadBasicType(is, binary, &f);
|
||||
*d = f;
|
||||
} else {
|
||||
KALDI_ERR << "ReadBasicType: expected float, saw " << is.peek()
|
||||
<< ", at file position " << is.tellg();
|
||||
}
|
||||
} else {
|
||||
is >> *d;
|
||||
}
|
||||
if (is.fail()) {
|
||||
KALDI_ERR << "ReadBasicType: failed to read, at file position "
|
||||
<< is.tellg();
|
||||
}
|
||||
}
|
||||
|
||||
void CheckToken(const char *token) {
|
||||
if (*token == '\0')
|
||||
KALDI_ERR << "Token is empty (not a valid token)";
|
||||
const char *orig_token = token;
|
||||
while (*token != '\0') {
|
||||
if (::isspace(*token))
|
||||
KALDI_ERR << "Token is not a valid token (contains space): '"
|
||||
<< orig_token << "'";
|
||||
token++;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteToken(std::ostream &os, bool binary, const char *token) {
|
||||
// binary mode is ignored;
|
||||
// we use space as termination character in either case.
|
||||
KALDI_ASSERT(token != NULL);
|
||||
CheckToken(token); // make sure it's valid (can be read back)
|
||||
os << token << " ";
|
||||
if (os.fail()) {
|
||||
KALDI_ERR << "Write failure in WriteToken.";
|
||||
}
|
||||
}
|
||||
|
||||
int Peek(std::istream &is, bool binary) {
|
||||
if (!binary) is >> std::ws; // eat up whitespace.
|
||||
return is.peek();
|
||||
}
|
||||
|
||||
void WriteToken(std::ostream &os, bool binary, const std::string & token) {
|
||||
WriteToken(os, binary, token.c_str());
|
||||
}
|
||||
|
||||
void ReadToken(std::istream &is, bool binary, std::string *str) {
|
||||
KALDI_ASSERT(str != NULL);
|
||||
if (!binary) is >> std::ws; // consume whitespace.
|
||||
is >> *str;
|
||||
if (is.fail()) {
|
||||
KALDI_ERR << "ReadToken, failed to read token at file position "
|
||||
<< is.tellg();
|
||||
}
|
||||
if (!isspace(is.peek())) {
|
||||
KALDI_ERR << "ReadToken, expected space after token, saw instead "
|
||||
<< CharToString(static_cast<char>(is.peek()))
|
||||
<< ", at file position " << is.tellg();
|
||||
}
|
||||
is.get(); // consume the space.
|
||||
}
|
||||
|
||||
int PeekToken(std::istream &is, bool binary) {
|
||||
if (!binary) is >> std::ws; // consume whitespace.
|
||||
bool read_bracket;
|
||||
if (static_cast<char>(is.peek()) == '<') {
|
||||
read_bracket = true;
|
||||
is.get();
|
||||
} else {
|
||||
read_bracket = false;
|
||||
}
|
||||
int ans = is.peek();
|
||||
if (read_bracket) {
|
||||
if (!is.unget()) {
|
||||
// Clear the bad bit. This code can be (and is in fact) reached, since the
|
||||
// C++ standard does not guarantee that a call to unget() must succeed.
|
||||
is.clear();
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
|
||||
void ExpectToken(std::istream &is, bool binary, const char *token) {
|
||||
int pos_at_start = is.tellg();
|
||||
KALDI_ASSERT(token != NULL);
|
||||
CheckToken(token); // make sure it's valid (can be read back)
|
||||
if (!binary) is >> std::ws; // consume whitespace.
|
||||
std::string str;
|
||||
is >> str;
|
||||
is.get(); // consume the space.
|
||||
if (is.fail()) {
|
||||
KALDI_ERR << "Failed to read token [started at file position "
|
||||
<< pos_at_start << "], expected " << token;
|
||||
}
|
||||
// The second half of the '&&' expression below is so that if we're expecting
|
||||
// "<Foo>", we will accept "Foo>" instead. This is so that the model-reading
|
||||
// code will tolerate errors in PeekToken where is.unget() failed; search for
|
||||
// is.clear() in PeekToken() for an explanation.
|
||||
if (strcmp(str.c_str(), token) != 0 &&
|
||||
!(token[0] == '<' && strcmp(str.c_str(), token + 1) == 0)) {
|
||||
KALDI_ERR << "Expected token \"" << token << "\", got instead \""
|
||||
<< str <<"\".";
|
||||
}
|
||||
}
|
||||
|
||||
void ExpectToken(std::istream &is, bool binary, const std::string &token) {
|
||||
ExpectToken(is, binary, token.c_str());
|
||||
}
|
||||
|
||||
} // end namespace kaldi
|
||||
@@ -0,0 +1,245 @@
|
||||
// base/io-funcs.h
|
||||
|
||||
// Copyright 2009-2011 Microsoft Corporation; Saarland University;
|
||||
// Jan Silovsky; Yanmin Qian
|
||||
// 2016 Xiaohui Zhang
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef KALDI_BASE_IO_FUNCS_H_
|
||||
#define KALDI_BASE_IO_FUNCS_H_
|
||||
|
||||
// This header only contains some relatively low-level I/O functions.
|
||||
// The full Kaldi I/O declarations are in ../util/kaldi-io.h
|
||||
// and ../util/kaldi-table.h
|
||||
// They were put in util/ in order to avoid making the Matrix library
|
||||
// dependent on them.
|
||||
|
||||
#include <cctype>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "base/kaldi-common.h"
|
||||
#include "base/io-funcs-inl.h"
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
|
||||
|
||||
/*
|
||||
This comment describes the Kaldi approach to I/O. All objects can be written
|
||||
and read in two modes: binary and text. In addition we want to make the I/O
|
||||
work if we redefine the typedef "BaseFloat" between floats and doubles.
|
||||
We also want to have control over whitespace in text mode without affecting
|
||||
the meaning of the file, for pretty-printing purposes.
|
||||
|
||||
Errors are handled by throwing a KaldiFatalError exception.
|
||||
|
||||
For integer and floating-point types (and boolean values):
|
||||
|
||||
WriteBasicType(std::ostream &, bool binary, const T&);
|
||||
ReadBasicType(std::istream &, bool binary, T*);
|
||||
|
||||
and we expect these functions to be defined in such a way that they work when
|
||||
the type T changes between float and double, so you can read float into double
|
||||
and vice versa]. Note that for efficiency and space-saving reasons, the Vector
|
||||
and Matrix classes do not use these functions [but they preserve the type
|
||||
interchangeability in their own way]
|
||||
|
||||
For a class (or struct) C:
|
||||
class C {
|
||||
..
|
||||
Write(std::ostream &, bool binary, [possibly extra optional args for specific classes]) const;
|
||||
Read(std::istream &, bool binary, [possibly extra optional args for specific classes]);
|
||||
..
|
||||
}
|
||||
NOTE: The only actual optional args we used are the "add" arguments in
|
||||
Vector/Matrix classes, which specify whether we should sum the data already
|
||||
in the class with the data being read.
|
||||
|
||||
For types which are typedef's involving stl classes, I/O is as follows:
|
||||
typedef std::vector<std::pair<A, B> > MyTypedefName;
|
||||
|
||||
The user should define something like:
|
||||
|
||||
WriteMyTypedefName(std::ostream &, bool binary, const MyTypedefName &t);
|
||||
ReadMyTypedefName(std::ostream &, bool binary, MyTypedefName *t);
|
||||
|
||||
The user would have to write these functions.
|
||||
|
||||
For a type std::vector<T>:
|
||||
|
||||
void WriteIntegerVector(std::ostream &os, bool binary, const std::vector<T> &v);
|
||||
void ReadIntegerVector(std::istream &is, bool binary, std::vector<T> *v);
|
||||
|
||||
For other types, e.g. vectors of pairs, the user should create a routine of the
|
||||
type WriteMyTypedefName. This is to avoid introducing confusing templated functions;
|
||||
we could easily create templated functions to handle most of these cases but they
|
||||
would have to share the same name.
|
||||
|
||||
It also often happens that the user needs to write/read special tokens as part
|
||||
of a file. These might be class headers, or separators/identifiers in the class.
|
||||
We provide special functions for manipulating these. These special tokens must
|
||||
be nonempty and must not contain any whitespace.
|
||||
|
||||
void WriteToken(std::ostream &os, bool binary, const char*);
|
||||
void WriteToken(std::ostream &os, bool binary, const std::string & token);
|
||||
int Peek(std::istream &is, bool binary);
|
||||
void ReadToken(std::istream &is, bool binary, std::string *str);
|
||||
void PeekToken(std::istream &is, bool binary, std::string *str);
|
||||
|
||||
WriteToken writes the token and one space (whether in binary or text mode).
|
||||
|
||||
Peek returns the first character of the next token, by consuming whitespace
|
||||
(in text mode) and then returning the peek() character. It returns -1 at EOF;
|
||||
it doesn't throw. It's useful if a class can have various forms based on
|
||||
typedefs and virtual classes, and wants to know which version to read.
|
||||
|
||||
ReadToken allows the caller to obtain the next token. PeekToken works just
|
||||
like ReadToken, but seeks back to the beginning of the token. A subsequent
|
||||
call to ReadToken will read the same token again. This is useful when
|
||||
different object types are written to the same file; using PeekToken one can
|
||||
decide which of the objects to read.
|
||||
|
||||
There is currently no special functionality for writing/reading strings (where the strings
|
||||
contain data rather than "special tokens" that are whitespace-free and nonempty). This is
|
||||
because Kaldi is structured in such a way that strings don't appear, except as OpenFst symbol
|
||||
table entries (and these have their own format).
|
||||
|
||||
|
||||
NOTE: you should not call ReadIntegerType and WriteIntegerType with types,
|
||||
such as int and size_t, that are machine-independent -- at least not
|
||||
if you want your file formats to port between machines. Use int32 and
|
||||
int64 where necessary. There is no way to detect this using compile-time
|
||||
assertions because C++ only keeps track of the internal representation of
|
||||
the type.
|
||||
*/
|
||||
|
||||
/// \addtogroup io_funcs_basic
|
||||
/// @{
|
||||
|
||||
|
||||
/// WriteBasicType is the name of the write function for bool, integer types,
|
||||
/// and floating-point types. They all throw on error.
|
||||
template<class T> void WriteBasicType(std::ostream &os, bool binary, T t);
|
||||
|
||||
/// ReadBasicType is the name of the read function for bool, integer types,
|
||||
/// and floating-point types. They all throw on error.
|
||||
template<class T> void ReadBasicType(std::istream &is, bool binary, T *t);
|
||||
|
||||
|
||||
// Declare specialization for bool.
|
||||
template<>
|
||||
void WriteBasicType<bool>(std::ostream &os, bool binary, bool b);
|
||||
|
||||
template <>
|
||||
void ReadBasicType<bool>(std::istream &is, bool binary, bool *b);
|
||||
|
||||
// Declare specializations for float and double.
|
||||
template<>
|
||||
void WriteBasicType<float>(std::ostream &os, bool binary, float f);
|
||||
|
||||
template<>
|
||||
void WriteBasicType<double>(std::ostream &os, bool binary, double f);
|
||||
|
||||
template<>
|
||||
void ReadBasicType<float>(std::istream &is, bool binary, float *f);
|
||||
|
||||
template<>
|
||||
void ReadBasicType<double>(std::istream &is, bool binary, double *f);
|
||||
|
||||
// Define ReadBasicType that accepts an "add" parameter to add to
|
||||
// the destination. Caution: if used in Read functions, be careful
|
||||
// to initialize the parameters concerned to zero in the default
|
||||
// constructor.
|
||||
template<class T>
|
||||
inline void ReadBasicType(std::istream &is, bool binary, T *t, bool add) {
|
||||
if (!add) {
|
||||
ReadBasicType(is, binary, t);
|
||||
} else {
|
||||
T tmp = T(0);
|
||||
ReadBasicType(is, binary, &tmp);
|
||||
*t += tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/// Function for writing STL vectors of integer types.
|
||||
template<class T> inline void WriteIntegerVector(std::ostream &os, bool binary,
|
||||
const std::vector<T> &v);
|
||||
|
||||
/// Function for reading STL vector of integer types.
|
||||
template<class T> inline void ReadIntegerVector(std::istream &is, bool binary,
|
||||
std::vector<T> *v);
|
||||
|
||||
/// Function for writing STL vectors of pairs of integer types.
|
||||
template<class T>
|
||||
inline void WriteIntegerPairVector(std::ostream &os, bool binary,
|
||||
const std::vector<std::pair<T, T> > &v);
|
||||
|
||||
/// Function for reading STL vector of pairs of integer types.
|
||||
template<class T>
|
||||
inline void ReadIntegerPairVector(std::istream &is, bool binary,
|
||||
std::vector<std::pair<T, T> > *v);
|
||||
|
||||
/// The WriteToken functions are for writing nonempty sequences of non-space
|
||||
/// characters. They are not for general strings.
|
||||
void WriteToken(std::ostream &os, bool binary, const char *token);
|
||||
void WriteToken(std::ostream &os, bool binary, const std::string & token);
|
||||
|
||||
/// Peek consumes whitespace (if binary == false) and then returns the peek()
|
||||
/// value of the stream.
|
||||
int Peek(std::istream &is, bool binary);
|
||||
|
||||
/// ReadToken gets the next token and puts it in str (exception on failure). If
|
||||
/// PeekToken() had been previously called, it is possible that the stream had
|
||||
/// failed to unget the starting '<' character. In this case ReadToken() returns
|
||||
/// the token string without the leading '<'. You must be prepared to handle
|
||||
/// this case. ExpectToken() handles this internally, and is not affected.
|
||||
void ReadToken(std::istream &is, bool binary, std::string *token);
|
||||
|
||||
/// PeekToken will return the first character of the next token, or -1 if end of
|
||||
/// file. It's the same as Peek(), except if the first character is '<' it will
|
||||
/// skip over it and will return the next character. It will attempt to unget
|
||||
/// the '<' so the stream is where it was before you did PeekToken(), however,
|
||||
/// this is not guaranteed (see ReadToken()).
|
||||
int PeekToken(std::istream &is, bool binary);
|
||||
|
||||
/// ExpectToken tries to read in the given token, and throws an exception
|
||||
/// on failure.
|
||||
void ExpectToken(std::istream &is, bool binary, const char *token);
|
||||
void ExpectToken(std::istream &is, bool binary, const std::string & token);
|
||||
|
||||
/// ExpectPretty attempts to read the text in "token", but only in non-binary
|
||||
/// mode. Throws exception on failure. It expects an exact match except that
|
||||
/// arbitrary whitespace matches arbitrary whitespace.
|
||||
void ExpectPretty(std::istream &is, bool binary, const char *token);
|
||||
void ExpectPretty(std::istream &is, bool binary, const std::string & token);
|
||||
|
||||
/// @} end "addtogroup io_funcs_basic"
|
||||
|
||||
|
||||
/// InitKaldiOutputStream initializes an opened stream for writing by writing an
|
||||
/// optional binary header and modifying the floating-point precision; it will
|
||||
/// typically not be called by users directly.
|
||||
inline void InitKaldiOutputStream(std::ostream &os, bool binary);
|
||||
|
||||
/// InitKaldiInputStream initializes an opened stream for reading by detecting
|
||||
/// the binary header and setting the "binary" value appropriately;
|
||||
/// It will typically not be called by users directly.
|
||||
inline bool InitKaldiInputStream(std::istream &is, bool *binary);
|
||||
|
||||
} // end namespace kaldi.
|
||||
#endif // KALDI_BASE_IO_FUNCS_H_
|
||||
@@ -0,0 +1,41 @@
|
||||
// base/kaldi-common.h
|
||||
|
||||
// Copyright 2009-2011 Microsoft Corporation
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef KALDI_BASE_KALDI_COMMON_H_
|
||||
#define KALDI_BASE_KALDI_COMMON_H_ 1
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring> // C string stuff like strcpy
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "base/kaldi-utils.h"
|
||||
#include "base/kaldi-error.h"
|
||||
#include "base/kaldi-types.h"
|
||||
#include "base/io-funcs.h"
|
||||
#include "base/kaldi-math.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
#endif // KALDI_BASE_KALDI_COMMON_H_
|
||||
@@ -0,0 +1,82 @@
|
||||
// base/kaldi-error-test.cc
|
||||
|
||||
// Copyright 2009-2011 Microsoft Corporation
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "base/kaldi-common.h"
|
||||
|
||||
// testing that we get the stack trace.
|
||||
namespace kaldi {
|
||||
|
||||
void MyFunction2() { KALDI_ERR << "Ignore this error"; }
|
||||
|
||||
void MyFunction1() { MyFunction2(); }
|
||||
|
||||
void UnitTestError() {
|
||||
{
|
||||
std::cerr << "Ignore next error:\n";
|
||||
MyFunction1();
|
||||
}
|
||||
}
|
||||
|
||||
void VerifySymbolRange(const std::string &trace, const bool want_found,
|
||||
const std::string &want_symbol) {
|
||||
size_t begin, end;
|
||||
const bool found = internal::LocateSymbolRange(trace, &begin, &end);
|
||||
if (found != want_found) {
|
||||
KALDI_ERR << "Found mismatch, got " << found << " want " << want_found;
|
||||
}
|
||||
if (!found) {
|
||||
return;
|
||||
}
|
||||
const std::string symbol = trace.substr(begin, end - begin);
|
||||
if (symbol != want_symbol) {
|
||||
KALDI_ERR << "Symbol mismatch, got " << symbol << " want " << want_symbol;
|
||||
}
|
||||
}
|
||||
|
||||
void TestLocateSymbolRange() {
|
||||
VerifySymbolRange("", false, "");
|
||||
VerifySymbolRange(
|
||||
R"TRACE(./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d])TRACE",
|
||||
true, "_ZN5kaldi13UnitTestErrorEv");
|
||||
// It is ok thread_start is not found because it is a C symbol.
|
||||
VerifySymbolRange(
|
||||
R"TRACE(31 libsystem_pthread.dylib 0x00007fff6fe4e40d thread_start + 13)TRACE",
|
||||
false, "");
|
||||
VerifySymbolRange(
|
||||
R"TRACE(0 server 0x000000010f67614d _ZNK5kaldi13MessageLogger10LogMessageEv + 813)TRACE",
|
||||
true, "_ZNK5kaldi13MessageLogger10LogMessageEv");
|
||||
VerifySymbolRange(
|
||||
R"TRACE(29 libsystem_pthread.dylib 0x00007fff6fe4f2eb _pthread_body + 126)TRACE",
|
||||
true, "_pthread_body");
|
||||
}
|
||||
|
||||
} // namespace kaldi
|
||||
|
||||
int main() {
|
||||
kaldi::TestLocateSymbolRange();
|
||||
|
||||
kaldi::SetProgramName("/foo/bar/kaldi-error-test");
|
||||
try {
|
||||
kaldi::UnitTestError();
|
||||
KALDI_ASSERT(0); // should not happen.
|
||||
exit(1);
|
||||
} catch (kaldi::KaldiFatalError &e) {
|
||||
std::cout << "The error we generated was: '" << e.KaldiMessage() << "'\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
// base/kaldi-error.cc
|
||||
|
||||
// Copyright 2019 LAIX (Yi Sun)
|
||||
// Copyright 2019 SmartAction LLC (kkm)
|
||||
// Copyright 2016 Brno University of Technology (author: Karel Vesely)
|
||||
// Copyright 2009-2011 Microsoft Corporation; Lukas Burget; Ondrej Glembek
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
#include <execinfo.h> // To get stack trace in error messages.
|
||||
// If this #include fails there is an error in the Makefile, it does not
|
||||
// support your platform well. Make sure HAVE_EXECINFO_H is undefined,
|
||||
// and the code will compile.
|
||||
#ifdef HAVE_CXXABI_H
|
||||
#include <cxxabi.h> // For name demangling.
|
||||
// Useful to decode the stack trace, but only used if we have execinfo.h
|
||||
#endif // HAVE_CXXABI_H
|
||||
#endif // HAVE_EXECINFO_H
|
||||
|
||||
#include "base/kaldi-common.h"
|
||||
#include "base/kaldi-error.h"
|
||||
|
||||
// KALDI_GIT_HEAD is useless currently in full repo
|
||||
//#if !defined(KALDI_VERSION)
|
||||
//#include "base/version.h"
|
||||
//#endif
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
/***** GLOBAL VARIABLES FOR LOGGING *****/
|
||||
|
||||
int32 g_kaldi_verbose_level = 0;
|
||||
static std::string program_name;
|
||||
static LogHandler log_handler = NULL;
|
||||
|
||||
void SetProgramName(const char *basename) {
|
||||
// Using the 'static std::string' for the program name is mostly harmless,
|
||||
// because (a) Kaldi logging is undefined before main(), and (b) no stdc++
|
||||
// string implementation has been found in the wild that would not be just
|
||||
// an empty string when zero-initialized but not yet constructed.
|
||||
program_name = basename;
|
||||
}
|
||||
|
||||
/***** HELPER FUNCTIONS *****/
|
||||
|
||||
// Trim filename to at most 1 trailing directory long. Given a filename like
|
||||
// "/a/b/c/d/e/f.cc", return "e/f.cc". Support both '/' and '\' as the path
|
||||
// separator.
|
||||
static const char *GetShortFileName(const char *path) {
|
||||
if (path == nullptr)
|
||||
return "";
|
||||
|
||||
const char *prev = path, *last = path;
|
||||
while ((path = std::strpbrk(path, "\\/")) != nullptr) {
|
||||
++path;
|
||||
prev = last;
|
||||
last = path;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
/***** STACK TRACE *****/
|
||||
|
||||
namespace internal {
|
||||
bool LocateSymbolRange(const std::string &trace_name, size_t *begin,
|
||||
size_t *end) {
|
||||
// Find the first '_' with leading ' ' or '('.
|
||||
*begin = std::string::npos;
|
||||
for (size_t i = 1; i < trace_name.size(); i++) {
|
||||
if (trace_name[i] != '_') {
|
||||
continue;
|
||||
}
|
||||
if (trace_name[i - 1] == ' ' || trace_name[i - 1] == '(') {
|
||||
*begin = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*begin == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
*end = trace_name.find_first_of(" +", *begin);
|
||||
return *end != std::string::npos;
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
static std::string Demangle(std::string trace_name) {
|
||||
#ifndef HAVE_CXXABI_H
|
||||
return trace_name;
|
||||
#else // HAVE_CXXABI_H
|
||||
// Try demangle the symbol. We are trying to support the following formats
|
||||
// produced by different platforms:
|
||||
//
|
||||
// Linux:
|
||||
// ./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d]
|
||||
//
|
||||
// Mac:
|
||||
// 0 server 0x000000010f67614d _ZNK5kaldi13MessageLogger10LogMessageEv + 813
|
||||
//
|
||||
// We want to extract the name e.g., '_ZN5kaldi13UnitTestErrorEv' and
|
||||
// demangle it info a readable name like kaldi::UnitTextError.
|
||||
size_t begin, end;
|
||||
if (!internal::LocateSymbolRange(trace_name, &begin, &end)) {
|
||||
return trace_name;
|
||||
}
|
||||
std::string symbol = trace_name.substr(begin, end - begin);
|
||||
int status;
|
||||
char *demangled_name = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status);
|
||||
if (status == 0 && demangled_name != nullptr) {
|
||||
symbol = demangled_name;
|
||||
free(demangled_name);
|
||||
}
|
||||
return trace_name.substr(0, begin) + symbol +
|
||||
trace_name.substr(end, std::string::npos);
|
||||
#endif // HAVE_CXXABI_H
|
||||
}
|
||||
#endif // HAVE_EXECINFO_H
|
||||
|
||||
static std::string KaldiGetStackTrace() {
|
||||
std::string ans;
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
const size_t KALDI_MAX_TRACE_SIZE = 50;
|
||||
const size_t KALDI_MAX_TRACE_PRINT = 50; // Must be even.
|
||||
// Buffer for the trace.
|
||||
void *trace[KALDI_MAX_TRACE_SIZE];
|
||||
// Get the trace.
|
||||
size_t size = backtrace(trace, KALDI_MAX_TRACE_SIZE);
|
||||
// Get the trace symbols.
|
||||
char **trace_symbol = backtrace_symbols(trace, size);
|
||||
if (trace_symbol == NULL)
|
||||
return ans;
|
||||
|
||||
// Compose a human-readable backtrace string.
|
||||
ans += "[ Stack-Trace: ]\n";
|
||||
if (size <= KALDI_MAX_TRACE_PRINT) {
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
ans += Demangle(trace_symbol[i]) + "\n";
|
||||
}
|
||||
} else { // Print out first+last (e.g.) 5.
|
||||
for (size_t i = 0; i < KALDI_MAX_TRACE_PRINT / 2; i++) {
|
||||
ans += Demangle(trace_symbol[i]) + "\n";
|
||||
}
|
||||
ans += ".\n.\n.\n";
|
||||
for (size_t i = size - KALDI_MAX_TRACE_PRINT / 2; i < size; i++) {
|
||||
ans += Demangle(trace_symbol[i]) + "\n";
|
||||
}
|
||||
if (size == KALDI_MAX_TRACE_SIZE)
|
||||
ans += ".\n.\n.\n"; // Stack was too long, probably a bug.
|
||||
}
|
||||
|
||||
// We must free the array of pointers allocated by backtrace_symbols(),
|
||||
// but not the strings themselves.
|
||||
free(trace_symbol);
|
||||
#endif // HAVE_EXECINFO_H
|
||||
return ans;
|
||||
}
|
||||
|
||||
/***** KALDI LOGGING *****/
|
||||
|
||||
MessageLogger::MessageLogger(LogMessageEnvelope::Severity severity,
|
||||
const char *func, const char *file, int32 line) {
|
||||
// Obviously, we assume the strings survive the destruction of this object.
|
||||
envelope_.severity = severity;
|
||||
envelope_.func = func;
|
||||
envelope_.file = GetShortFileName(file); // Points inside 'file'.
|
||||
envelope_.line = line;
|
||||
}
|
||||
|
||||
void MessageLogger::LogMessage() const {
|
||||
// Send to the logging handler if provided.
|
||||
if (log_handler != NULL) {
|
||||
log_handler(envelope_, GetMessage().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, use the default Kaldi logging.
|
||||
// Build the log-message header.
|
||||
std::stringstream full_message;
|
||||
if (envelope_.severity > LogMessageEnvelope::kInfo) {
|
||||
full_message << "VLOG[" << envelope_.severity << "] (";
|
||||
} else {
|
||||
switch (envelope_.severity) {
|
||||
case LogMessageEnvelope::kInfo:
|
||||
//full_message << "LOG (\n";
|
||||
break;
|
||||
case LogMessageEnvelope::kWarning:
|
||||
//full_message << "WARNING (\n";
|
||||
break;
|
||||
case LogMessageEnvelope::kAssertFailed:
|
||||
full_message << "ASSERTION_FAILED (\n";
|
||||
break;
|
||||
case LogMessageEnvelope::kError:
|
||||
default: // If not the ERROR, it still an error!
|
||||
full_message << "ERROR (\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Add other info from the envelope and the message text.
|
||||
//full_message << program_name.c_str() << "[" KALDI_VERSION "]" << ':'
|
||||
// << envelope_.func << "():" << envelope_.file << ':'
|
||||
// << envelope_.line << ") " << GetMessage().c_str();
|
||||
|
||||
// Add stack trace for errors and assertion failures, if available.
|
||||
if (envelope_.severity < LogMessageEnvelope::kWarning) {
|
||||
const std::string &stack_trace = KaldiGetStackTrace();
|
||||
if (!stack_trace.empty()) {
|
||||
full_message << "\n\n" << stack_trace;
|
||||
}
|
||||
}
|
||||
|
||||
// Print the complete message to stderr.
|
||||
std::cerr << full_message.str();
|
||||
}
|
||||
|
||||
/***** KALDI ASSERTS *****/
|
||||
|
||||
void KaldiAssertFailure_(const char *func, const char *file, int32 line,
|
||||
const char *cond_str) {
|
||||
MessageLogger::Log() =
|
||||
MessageLogger(LogMessageEnvelope::kAssertFailed, func, file, line)
|
||||
<< "Assertion failed: (" << cond_str << ")";
|
||||
fflush(NULL); // Flush all pending buffers, abort() may not flush stderr.
|
||||
std::abort();
|
||||
}
|
||||
|
||||
/***** THIRD-PARTY LOG-HANDLER *****/
|
||||
|
||||
LogHandler SetLogHandler(LogHandler handler) {
|
||||
LogHandler old_handler = log_handler;
|
||||
log_handler = handler;
|
||||
return old_handler;
|
||||
}
|
||||
|
||||
} // namespace kaldi
|
||||
@@ -0,0 +1,231 @@
|
||||
// base/kaldi-error.h
|
||||
|
||||
// Copyright 2019 LAIX (Yi Sun)
|
||||
// Copyright 2019 SmartAction LLC (kkm)
|
||||
// Copyright 2016 Brno University of Technology (author: Karel Vesely)
|
||||
// Copyright 2009-2011 Microsoft Corporation; Ondrej Glembek; Lukas Burget;
|
||||
// Saarland University
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef KALDI_BASE_KALDI_ERROR_H_
|
||||
#define KALDI_BASE_KALDI_ERROR_H_ 1
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/kaldi-types.h"
|
||||
#include "base/kaldi-utils.h"
|
||||
/* Important that this file does not depend on any other kaldi headers. */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define __func__ __FUNCTION__
|
||||
#endif
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
/// \addtogroup error_group
|
||||
/// @{
|
||||
|
||||
/***** PROGRAM NAME AND VERBOSITY LEVEL *****/
|
||||
|
||||
/// Called by ParseOptions to set base name (no directory) of the executing
|
||||
/// program. The name is printed in logging code along with every message,
|
||||
/// because in our scripts, we often mix together the stderr of many programs.
|
||||
/// This function is very thread-unsafe.
|
||||
void SetProgramName(const char *basename);
|
||||
|
||||
/// This is set by util/parse-options.{h,cc} if you set --verbose=? option.
|
||||
/// Do not use directly, prefer {Get,Set}VerboseLevel().
|
||||
extern int32 g_kaldi_verbose_level;
|
||||
|
||||
/// Get verbosity level, usually set via command line '--verbose=' switch.
|
||||
inline int32 GetVerboseLevel() { return g_kaldi_verbose_level; }
|
||||
|
||||
/// This should be rarely used, except by programs using Kaldi as library;
|
||||
/// command-line programs set the verbose level automatically from ParseOptions.
|
||||
inline void SetVerboseLevel(int32 i) { g_kaldi_verbose_level = i; }
|
||||
|
||||
/***** KALDI LOGGING *****/
|
||||
|
||||
/// Log message severity and source location info.
|
||||
struct LogMessageEnvelope {
|
||||
/// Message severity. In addition to these levels, positive values (1 to 6)
|
||||
/// specify verbose logging level. Verbose messages are produced only when
|
||||
/// SetVerboseLevel() has been called to set logging level to at least the
|
||||
/// corresponding value.
|
||||
enum Severity {
|
||||
kAssertFailed = -3, //!< Assertion failure. abort() will be called.
|
||||
kError = -2, //!< Fatal error. KaldiFatalError will be thrown.
|
||||
kWarning = -1, //!< Indicates a recoverable but abnormal condition.
|
||||
kInfo = 0, //!< Informational message.
|
||||
};
|
||||
int severity; //!< A Severity value, or positive verbosity level.
|
||||
const char *func; //!< Name of the function invoking the logging.
|
||||
const char *file; //!< Source file name with up to 1 leading directory.
|
||||
int32 line; //<! Line number in the source file.
|
||||
};
|
||||
|
||||
/// Kaldi fatal runtime error exception. This exception is thrown from any use
|
||||
/// of the KALDI_ERR logging macro after the logging function, either set by
|
||||
/// SetLogHandler(), or the Kaldi's internal one, has returned.
|
||||
class KaldiFatalError : public std::runtime_error {
|
||||
public:
|
||||
explicit KaldiFatalError(const std::string &message)
|
||||
: std::runtime_error(message) {}
|
||||
explicit KaldiFatalError(const char *message) : std::runtime_error(message) {}
|
||||
|
||||
/// Returns the exception name, "kaldi::KaldiFatalError".
|
||||
virtual const char *what() const noexcept override {
|
||||
return "kaldi::KaldiFatalError";
|
||||
}
|
||||
|
||||
/// Returns the Kaldi error message logged by KALDI_ERR.
|
||||
const char *KaldiMessage() const { return std::runtime_error::what(); }
|
||||
};
|
||||
|
||||
// Class MessageLogger is the workhorse behind the KALDI_ASSERT, KALDI_ERR,
|
||||
// KALDI_WARN, KALDI_LOG and KALDI_VLOG macros. It formats the message, then
|
||||
// either prints it to stderr or passes to the custom logging handler if
|
||||
// provided. Then, in case of the error, throws a KaldiFatalError exception, or
|
||||
// in case of failed KALDI_ASSERT, calls std::abort().
|
||||
class MessageLogger {
|
||||
public:
|
||||
/// The constructor stores the message's "envelope", a set of data which
|
||||
// identifies the location in source which is sending the message to log.
|
||||
// The pointers to strings are stored internally, and not owned or copied,
|
||||
// so that their storage must outlive this object.
|
||||
MessageLogger(LogMessageEnvelope::Severity severity, const char *func,
|
||||
const char *file, int32 line);
|
||||
|
||||
// The stream insertion operator, used in e.g. 'KALDI_LOG << "Message"'.
|
||||
template <typename T> MessageLogger &operator<<(const T &val) {
|
||||
ss_ << val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// When assigned a MessageLogger, log its contents.
|
||||
struct Log final {
|
||||
void operator=(const MessageLogger &logger) { logger.LogMessage(); }
|
||||
};
|
||||
|
||||
// When assigned a MessageLogger, log its contents and then throw
|
||||
// a KaldiFatalError.
|
||||
struct LogAndThrow final {
|
||||
[[noreturn]] void operator=(const MessageLogger &logger) {
|
||||
logger.LogMessage();
|
||||
throw KaldiFatalError(logger.GetMessage());
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
std::string GetMessage() const { return ss_.str(); }
|
||||
void LogMessage() const;
|
||||
|
||||
LogMessageEnvelope envelope_;
|
||||
std::ostringstream ss_;
|
||||
};
|
||||
|
||||
// Logging macros.
|
||||
#define KALDI_ERR \
|
||||
::kaldi::MessageLogger::LogAndThrow() = ::kaldi::MessageLogger( \
|
||||
::kaldi::LogMessageEnvelope::kError, __func__, __FILE__, __LINE__)
|
||||
#define KALDI_WARN \
|
||||
::kaldi::MessageLogger::Log() = ::kaldi::MessageLogger( \
|
||||
::kaldi::LogMessageEnvelope::kWarning, __func__, __FILE__, __LINE__)
|
||||
#define KALDI_LOG \
|
||||
::kaldi::MessageLogger::Log() = ::kaldi::MessageLogger( \
|
||||
::kaldi::LogMessageEnvelope::kInfo, __func__, __FILE__, __LINE__)
|
||||
#define KALDI_VLOG(v) \
|
||||
if ((v) <= ::kaldi::GetVerboseLevel()) \
|
||||
::kaldi::MessageLogger::Log() = \
|
||||
::kaldi::MessageLogger((::kaldi::LogMessageEnvelope::Severity)(v), \
|
||||
__func__, __FILE__, __LINE__)
|
||||
|
||||
/***** KALDI ASSERTS *****/
|
||||
|
||||
[[noreturn]] void KaldiAssertFailure_(const char *func, const char *file,
|
||||
int32 line, const char *cond_str);
|
||||
|
||||
// Note on KALDI_ASSERT and KALDI_PARANOID_ASSERT:
|
||||
//
|
||||
// A single block {} around if /else does not work, because it causes
|
||||
// syntax error (unmatched else block) in the following code:
|
||||
//
|
||||
// if (condition)
|
||||
// KALDI_ASSERT(condition2);
|
||||
// else
|
||||
// SomethingElse();
|
||||
//
|
||||
// do {} while(0) -- note there is no semicolon at the end! -- works nicely,
|
||||
// and compilers will be able to optimize the loop away (as the condition
|
||||
// is always false).
|
||||
//
|
||||
// Also see KALDI_COMPILE_TIME_ASSERT, defined in base/kaldi-utils.h, and
|
||||
// KALDI_ASSERT_IS_INTEGER_TYPE and KALDI_ASSERT_IS_FLOATING_TYPE, also defined
|
||||
// there.
|
||||
#ifndef NDEBUG
|
||||
#define KALDI_ASSERT(cond) \
|
||||
do { \
|
||||
if (cond) \
|
||||
(void)0; \
|
||||
else \
|
||||
::kaldi::KaldiAssertFailure_(__func__, __FILE__, __LINE__, #cond); \
|
||||
} while (0)
|
||||
#else
|
||||
#define KALDI_ASSERT(cond) (void)0
|
||||
#endif
|
||||
|
||||
// Some more expensive asserts only checked if this defined.
|
||||
#ifdef KALDI_PARANOID
|
||||
#define KALDI_PARANOID_ASSERT(cond) \
|
||||
do { \
|
||||
if (cond) \
|
||||
(void)0; \
|
||||
else \
|
||||
::kaldi::KaldiAssertFailure_(__func__, __FILE__, __LINE__, #cond); \
|
||||
} while (0)
|
||||
#else
|
||||
#define KALDI_PARANOID_ASSERT(cond) (void)0
|
||||
#endif
|
||||
|
||||
/***** THIRD-PARTY LOG-HANDLER *****/
|
||||
|
||||
/// Type of third-party logging function.
|
||||
typedef void (*LogHandler)(const LogMessageEnvelope &envelope,
|
||||
const char *message);
|
||||
|
||||
/// Set logging handler. If called with a non-NULL function pointer, the
|
||||
/// function pointed by it is called to send messages to a caller-provided log.
|
||||
/// If called with a NULL pointer, restores default Kaldi error logging to
|
||||
/// stderr. This function is obviously not thread safe; the log handler must be.
|
||||
/// Returns a previously set logging handler pointer, or NULL.
|
||||
LogHandler SetLogHandler(LogHandler);
|
||||
|
||||
/// @} end "addtogroup error_group"
|
||||
|
||||
//// Functions within internal is exported for testing only, do not use.
|
||||
//namespace internal {
|
||||
//bool LocateSymbolRange(const std::string &trace_name, size_t *begin,
|
||||
// size_t *end);
|
||||
//} // namespace internal
|
||||
} // namespace kaldi
|
||||
|
||||
#endif // KALDI_BASE_KALDI_ERROR_H_
|
||||
@@ -0,0 +1,334 @@
|
||||
// base/kaldi-math-test.cc
|
||||
//
|
||||
// Copyright 2009-2011 Microsoft Corporation; Yanmin Qian; Jan Silovsky
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include "base/kaldi-math.h"
|
||||
#include <limits>
|
||||
#include "base/timer.h"
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
template<class I> void UnitTestGcdLcmTpl() {
|
||||
for (I a = 1; a < 15; a++) { // a is min gcd.
|
||||
I b = (I)(Rand() % 10);
|
||||
I c = (I)(Rand() % 10);
|
||||
if (std::numeric_limits<I>::is_signed) {
|
||||
if (Rand() % 2 == 0) b = -b;
|
||||
if (Rand() % 2 == 0) c = -c;
|
||||
}
|
||||
if (b == 0 && c == 0) continue; // gcd not defined for such numbers.
|
||||
I g = Gcd(b*a, c*a);
|
||||
KALDI_ASSERT(g >= a);
|
||||
KALDI_ASSERT((b*a) % g == 0);
|
||||
KALDI_ASSERT((c*a) % g == 0);
|
||||
|
||||
// test least common multiple
|
||||
if (b <= 0 || c <= 0) continue; // lcm not defined unless both positive.
|
||||
I h = Lcm(b*a, c*a);
|
||||
KALDI_ASSERT(h != 0 && (h % (b*a)) == 0 &&
|
||||
(h % (c*a)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void UnitTestRoundUpToNearestPowerOfTwo() {
|
||||
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(1) == 1);
|
||||
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(2) == 2);
|
||||
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(3) == 4);
|
||||
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(4) == 4);
|
||||
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(7) == 8);
|
||||
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(8) == 8);
|
||||
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(255) == 256);
|
||||
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(256) == 256);
|
||||
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(257) == 512);
|
||||
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(1073700000) == 1073741824);
|
||||
}
|
||||
|
||||
void UnitTestDivideRoundingDown() {
|
||||
for (int32 i = 0; i < 100; i++) {
|
||||
int32 a = RandInt(-100, 100);
|
||||
int32 b = 0;
|
||||
while (b == 0)
|
||||
b = RandInt(-100, 100);
|
||||
KALDI_ASSERT(DivideRoundingDown(a, b) ==
|
||||
std::floor(static_cast<double>(a) / static_cast<double>(b)));
|
||||
}
|
||||
}
|
||||
|
||||
void UnitTestGcdLcm() {
|
||||
UnitTestGcdLcmTpl<int>();
|
||||
UnitTestGcdLcmTpl<size_t>();
|
||||
UnitTestGcdLcmTpl<int16>();
|
||||
}
|
||||
|
||||
void UnitTestRand() {
|
||||
// Testing random-number generation.
|
||||
std::cout << "Testing random-number generation. "
|
||||
<< "If there is an error this may not terminate.\n";
|
||||
std::cout << "If this does not terminate, look more closely. "
|
||||
<< "There might be a problem [but might not be]\n";
|
||||
for (int i = 1; i < 10; i++) {
|
||||
{ // test RandUniform.
|
||||
std::cout << "Test RandUniform\n";
|
||||
KALDI_ASSERT(RandUniform() >= 0 && RandUniform() <= 1);
|
||||
float sum = RandUniform()-0.5;
|
||||
for (int j = 0; ; j++) {
|
||||
sum += RandUniform()-0.5;
|
||||
if (std::abs(sum) < 0.5*sqrt(static_cast<double>(j))) break;
|
||||
}
|
||||
}
|
||||
{ // test RandGauss.
|
||||
float sum = RandGauss();
|
||||
for (int j = 0; ; j++) {
|
||||
sum += RandGauss();
|
||||
if (std::abs(sum) < 0.5*sqrt(static_cast<double>(j))) break;
|
||||
}
|
||||
}
|
||||
{ // test RandGauss.
|
||||
float sum = RandGauss();
|
||||
for (int j = 0; ; j++) {
|
||||
float a, b;
|
||||
RandGauss2(&a, &b);
|
||||
if (i % 2 == 0) sum += a;
|
||||
else
|
||||
sum += b;
|
||||
if (std::abs(sum) < 0.5*sqrt(static_cast<double>(j))) break;
|
||||
}
|
||||
}
|
||||
{ // test poisson_Rand().
|
||||
KALDI_ASSERT(RandPoisson(3.0) >= 0);
|
||||
KALDI_ASSERT(RandPoisson(0.0) == 0);
|
||||
std::cout << "Test RandPoisson\n";
|
||||
float lambda = RandUniform() * 3.0; // between 0 and 3.
|
||||
double sum = RandPoisson(lambda) - lambda; // expected value is zero.
|
||||
for (int j = 0; ; j++) {
|
||||
sum += RandPoisson(lambda) - lambda;
|
||||
if (std::abs(sum) < 0.5*sqrt(static_cast<double>(j))) break;
|
||||
}
|
||||
}
|
||||
|
||||
{ // test WithProb().
|
||||
for (int32 i = 0; i < 10; i++) {
|
||||
KALDI_ASSERT((WithProb(0.0) == false) && (WithProb(1.0) == true));
|
||||
}
|
||||
{
|
||||
int32 tot = 0, n = 10000;
|
||||
BaseFloat p = 0.5;
|
||||
for (int32 i = 0; i < n; i++)
|
||||
tot += WithProb(p);
|
||||
KALDI_ASSERT(tot > (n * p * 0.8) && tot < (n * p * 1.2));
|
||||
}
|
||||
{
|
||||
int32 tot = 0, n = 10000;
|
||||
BaseFloat p = 0.25;
|
||||
for (int32 i = 0; i < n; i++)
|
||||
tot += WithProb(p);
|
||||
KALDI_ASSERT(tot > (n * p * 0.8) && tot < (n * p * 1.2));
|
||||
}
|
||||
}
|
||||
{ // test RandInt().
|
||||
KALDI_ASSERT(RandInt(0, 3) >= 0 && RandInt(0, 3) <= 3);
|
||||
|
||||
std::cout << "Test RandInt\n";
|
||||
int minint = Rand() % 200;
|
||||
int maxint = minint + 1 + Rand() % 20;
|
||||
|
||||
float sum = RandInt(minint, maxint) + 0.5*(minint+maxint);
|
||||
for (int j = 0; ; j++) {
|
||||
sum += RandInt(minint, maxint) - 0.5*(minint+maxint);
|
||||
if (std::abs(static_cast<float>(sum)) <
|
||||
0.5*sqrt(static_cast<double>(j))*(maxint-minint)) break;
|
||||
}
|
||||
}
|
||||
{ // test RandPrune in basic way.
|
||||
KALDI_ASSERT(RandPrune(1.1, 1.0) == 1.1);
|
||||
KALDI_ASSERT(RandPrune(0.0, 0.0) == 0.0);
|
||||
KALDI_ASSERT(RandPrune(-1.1, 1.0) == -1.1);
|
||||
KALDI_ASSERT(RandPrune(0.0, 1.0) == 0.0);
|
||||
KALDI_ASSERT(RandPrune(0.5, 1.0) >= 0.0);
|
||||
KALDI_ASSERT(RandPrune(-0.5, 1.0) <= 0.0);
|
||||
BaseFloat f = RandPrune(-0.5, 1.0);
|
||||
KALDI_ASSERT(f == 0.0 || f == -1.0);
|
||||
f = RandPrune(0.5, 1.0);
|
||||
KALDI_ASSERT(f == 0.0 || f == 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnitTestLogAddSub() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
double f1 = Rand() % 10000, f2 = Rand() % 20;
|
||||
double add1 = Exp(LogAdd(Log(f1), Log(f2)));
|
||||
double add2 = Exp(LogAdd(Log(f2), Log(f1)));
|
||||
double add = f1 + f2, thresh = add*0.00001;
|
||||
KALDI_ASSERT(std::abs(add-add1) < thresh && std::abs(add-add2) < thresh);
|
||||
|
||||
|
||||
try {
|
||||
double f2_check = Exp(LogSub(Log(add), Log(f1))),
|
||||
thresh = (f2*0.01)+0.001;
|
||||
KALDI_ASSERT(std::abs(f2_check-f2) < thresh);
|
||||
} catch(...) {
|
||||
KALDI_ASSERT(f2 == 0); // It will probably crash for f2=0.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnitTestDefines() { // Yes, we even unit-test the preprocessor statements.
|
||||
KALDI_ASSERT(Exp(kLogZeroFloat) == 0.0);
|
||||
KALDI_ASSERT(Exp(kLogZeroDouble) == 0.0);
|
||||
BaseFloat den = 0.0;
|
||||
KALDI_ASSERT(KALDI_ISNAN(0.0 / den));
|
||||
KALDI_ASSERT(!KALDI_ISINF(0.0 / den));
|
||||
KALDI_ASSERT(!KALDI_ISFINITE(0.0 / den));
|
||||
KALDI_ASSERT(!KALDI_ISNAN(1.0 / den));
|
||||
KALDI_ASSERT(KALDI_ISINF(1.0 / den));
|
||||
KALDI_ASSERT(!KALDI_ISFINITE(1.0 / den));
|
||||
KALDI_ASSERT(KALDI_ISFINITE(0.0));
|
||||
KALDI_ASSERT(!KALDI_ISINF(0.0));
|
||||
KALDI_ASSERT(!KALDI_ISNAN(0.0));
|
||||
|
||||
std::cout << 1.0+DBL_EPSILON;
|
||||
std::cout << 1.0 + 0.5*DBL_EPSILON;
|
||||
KALDI_ASSERT(1.0 + DBL_EPSILON != 1.0 && 1.0 + (0.5*DBL_EPSILON) == 1.0
|
||||
&& "If this test fails, you can probably just comment it out-- "
|
||||
"may mean your CPU exceeds expected floating point precision");
|
||||
KALDI_ASSERT(1.0f + FLT_EPSILON != 1.0f && 1.0f + (0.5f*FLT_EPSILON) == 1.0f
|
||||
&& "If this test fails, you can probably just comment it out-- "
|
||||
"may mean your CPU exceeds expected floating point precision");
|
||||
KALDI_ASSERT(std::abs(sin(M_PI)) < 1.0e-05
|
||||
&& std::abs(cos(M_PI)+1.0) < 1.0e-05);
|
||||
KALDI_ASSERT(std::abs(sin(M_2PI)) < 1.0e-05
|
||||
&& std::abs(cos(M_2PI)-1.0) < 1.0e-05);
|
||||
KALDI_ASSERT(std::abs(sin(Exp(M_LOG_2PI))) < 1.0e-05);
|
||||
KALDI_ASSERT(std::abs(cos(Exp(M_LOG_2PI)) - 1.0) < 1.0e-05);
|
||||
}
|
||||
|
||||
void UnitTestAssertFunc() { // Testing Assert** *functions
|
||||
for (int i = 1; i < 100; i++) {
|
||||
float f1 = Rand() % 10000 + 1, f2 = Rand() % 20 + 1;
|
||||
float tmp1 = f1 * f2;
|
||||
float tmp2 = (1/f1 + 1/f2);
|
||||
float add = f1 + f2;
|
||||
float addeql = tmp1 * tmp2;
|
||||
float thresh = 0.00001;
|
||||
AssertEqual(add, addeql, thresh); // test AssertEqual()
|
||||
}
|
||||
}
|
||||
|
||||
template<class I> void UnitTestFactorizeTpl() {
|
||||
for (int p= 0; p < 100; p++) {
|
||||
I m = Rand() % 100000;
|
||||
if (m >= 1) {
|
||||
std::vector<I> factors;
|
||||
Factorize(m, &factors);
|
||||
I m2 = 1;
|
||||
for (size_t i = 0; i < factors.size(); i++) {
|
||||
m2 *= factors[i];
|
||||
if (i+1 < factors.size())
|
||||
KALDI_ASSERT(factors[i+1] >= factors[i]); // check sorted.
|
||||
}
|
||||
KALDI_ASSERT(m2 == m); // check correctness.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnitTestFactorize() {
|
||||
UnitTestFactorizeTpl<int>();
|
||||
UnitTestFactorizeTpl<size_t>();
|
||||
UnitTestFactorizeTpl<int16>();
|
||||
}
|
||||
|
||||
void UnitTestApproxEqual() {
|
||||
KALDI_ASSERT(ApproxEqual(1.0, 1.00001));
|
||||
KALDI_ASSERT(ApproxEqual(1.0, 1.00001, 0.001));
|
||||
KALDI_ASSERT(!ApproxEqual(1.0, 1.1));
|
||||
KALDI_ASSERT(!ApproxEqual(1.0, 1.01, 0.001));
|
||||
KALDI_ASSERT(!ApproxEqual(1.0, 0.0));
|
||||
KALDI_ASSERT(ApproxEqual(0.0, 0.0));
|
||||
KALDI_ASSERT(!ApproxEqual(0.0, 0.00001));
|
||||
KALDI_ASSERT(!ApproxEqual(std::numeric_limits<float>::infinity(),
|
||||
-std::numeric_limits<float>::infinity()));
|
||||
KALDI_ASSERT(ApproxEqual(std::numeric_limits<float>::infinity(),
|
||||
std::numeric_limits<float>::infinity()));
|
||||
KALDI_ASSERT(ApproxEqual(-std::numeric_limits<float>::infinity(),
|
||||
-std::numeric_limits<float>::infinity()));
|
||||
KALDI_ASSERT(!ApproxEqual(-std::numeric_limits<float>::infinity(),
|
||||
0));
|
||||
KALDI_ASSERT(!ApproxEqual(-std::numeric_limits<float>::infinity(),
|
||||
1));
|
||||
}
|
||||
|
||||
template<class Real>
|
||||
void UnitTestExpSpeed() {
|
||||
Real sum = 0.0; // compute the sum to avoid optimizing it away.
|
||||
Real time = 0.01; // how long this should last.
|
||||
int block_size = 10;
|
||||
int num_ops = 0;
|
||||
Timer tim;
|
||||
while (tim.Elapsed() < time) {
|
||||
for (int i = 0; i < block_size; i++) {
|
||||
sum += Exp((Real)i);
|
||||
}
|
||||
num_ops += block_size;
|
||||
}
|
||||
KALDI_ASSERT(sum > 0.0); // make it harder for the compiler to optimize Exp
|
||||
// away, as we have a conditional.
|
||||
Real flops = 1.0e-06 * num_ops / tim.Elapsed();
|
||||
KALDI_LOG << "Megaflops doing Exp("
|
||||
<< (sizeof(Real) == 4 ? "float" : "double") << ") is " << flops;
|
||||
}
|
||||
|
||||
|
||||
template<class Real>
|
||||
void UnitTestLogSpeed() {
|
||||
Real sum = 0.0; // compute the sum to avoid optimizing it away.
|
||||
Real time = 0.01; // how long this should last.
|
||||
int block_size = 10;
|
||||
int num_ops = 0;
|
||||
Timer tim;
|
||||
while (tim.Elapsed() < time) {
|
||||
for (int i = 0; i < block_size; i++) {
|
||||
sum += Log(static_cast<float>(i + 1));
|
||||
}
|
||||
num_ops += block_size;
|
||||
}
|
||||
KALDI_ASSERT(sum > 0.0); // make it harder for the compiler to optimize Log
|
||||
// away, as we have a conditional.
|
||||
Real flops = 1.0e-06 * num_ops / tim.Elapsed();
|
||||
KALDI_LOG << "Megaflops doing Log("
|
||||
<< (sizeof(Real) == 4 ? "float" : "double") << ") is " << flops;
|
||||
}
|
||||
|
||||
} // end namespace kaldi.
|
||||
|
||||
int main() {
|
||||
using namespace kaldi;
|
||||
UnitTestApproxEqual();
|
||||
UnitTestGcdLcm();
|
||||
UnitTestFactorize();
|
||||
UnitTestDefines();
|
||||
UnitTestLogAddSub();
|
||||
UnitTestRand();
|
||||
UnitTestAssertFunc();
|
||||
UnitTestRoundUpToNearestPowerOfTwo();
|
||||
UnitTestDivideRoundingDown();
|
||||
UnitTestExpSpeed<float>();
|
||||
UnitTestExpSpeed<double>();
|
||||
UnitTestLogSpeed<float>();
|
||||
UnitTestLogSpeed<double>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
// base/kaldi-math.cc
|
||||
|
||||
// Copyright 2009-2011 Microsoft Corporation; Yanmin Qian;
|
||||
// Saarland University; Jan Silovsky
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "base/kaldi-math.h"
|
||||
#ifndef _MSC_VER
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
namespace kaldi {
|
||||
// These routines are tested in matrix/matrix-test.cc
|
||||
|
||||
int32 RoundUpToNearestPowerOfTwo(int32 n) {
|
||||
KALDI_ASSERT(n > 0);
|
||||
n--;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
return n+1;
|
||||
}
|
||||
|
||||
static std::mutex _RandMutex;
|
||||
|
||||
int Rand(struct RandomState* state) {
|
||||
#if !defined(_POSIX_THREAD_SAFE_FUNCTIONS)
|
||||
// On Windows and Cygwin, just call Rand()
|
||||
return rand();
|
||||
#else
|
||||
if (state) {
|
||||
return rand_r(&(state->seed));
|
||||
} else {
|
||||
std::lock_guard<std::mutex> lock(_RandMutex);
|
||||
return rand();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
RandomState::RandomState() {
|
||||
// we initialize it as Rand() + 27437 instead of just Rand(), because on some
|
||||
// systems, e.g. at the very least Mac OSX Yosemite and later, it seems to be
|
||||
// the case that rand_r when initialized with rand() will give you the exact
|
||||
// same sequence of numbers that rand() will give if you keep calling rand()
|
||||
// after that initial call. This can cause problems with repeated sequences.
|
||||
// For example if you initialize two RandomState structs one after the other
|
||||
// without calling rand() in between, they would give you the same sequence
|
||||
// offset by one (if we didn't have the "+ 27437" in the code). 27437 is just
|
||||
// a randomly chosen prime number.
|
||||
seed = unsigned(Rand()) + 27437;
|
||||
}
|
||||
|
||||
bool WithProb(BaseFloat prob, struct RandomState* state) {
|
||||
KALDI_ASSERT(prob >= 0 && prob <= 1.1); // prob should be <= 1.0,
|
||||
// but we allow slightly larger values that could arise from roundoff in
|
||||
// previous calculations.
|
||||
KALDI_COMPILE_TIME_ASSERT(RAND_MAX > 128 * 128);
|
||||
if (prob == 0) return false;
|
||||
else if (prob == 1.0) return true;
|
||||
else if (prob * RAND_MAX < 128.0) {
|
||||
// prob is very small but nonzero, and the "main algorithm"
|
||||
// wouldn't work that well. So: with probability 1/128, we
|
||||
// return WithProb (prob * 128), else return false.
|
||||
if (Rand(state) < RAND_MAX / 128) { // with probability 128...
|
||||
// Note: we know that prob * 128.0 < 1.0, because
|
||||
// we asserted RAND_MAX > 128 * 128.
|
||||
return WithProb(prob * 128.0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return (Rand(state) < ((RAND_MAX + static_cast<BaseFloat>(1.0)) * prob));
|
||||
}
|
||||
}
|
||||
|
||||
int32 RandInt(int32 min_val, int32 max_val, struct RandomState* state) {
|
||||
// This is not exact.
|
||||
KALDI_ASSERT(max_val >= min_val);
|
||||
if (max_val == min_val) return min_val;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// RAND_MAX is quite small on Windows -> may need to handle larger numbers.
|
||||
if (RAND_MAX > (max_val-min_val)*8) {
|
||||
// *8 to avoid large inaccuracies in probability, from the modulus...
|
||||
return min_val +
|
||||
((unsigned int)Rand(state) % (unsigned int)(max_val+1-min_val));
|
||||
} else {
|
||||
if ((unsigned int)(RAND_MAX*RAND_MAX) >
|
||||
(unsigned int)((max_val+1-min_val)*8)) {
|
||||
// *8 to avoid inaccuracies in probability, from the modulus...
|
||||
return min_val + ( (unsigned int)( (Rand(state)+RAND_MAX*Rand(state)))
|
||||
% (unsigned int)(max_val+1-min_val));
|
||||
} else {
|
||||
KALDI_ERR << "rand_int failed because we do not support such large "
|
||||
"random numbers. (Extend this function).";
|
||||
}
|
||||
}
|
||||
#else
|
||||
return min_val +
|
||||
(static_cast<int32>(Rand(state)) % static_cast<int32>(max_val+1-min_val));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns poisson-distributed random number.
|
||||
// Take care: this takes time proportional
|
||||
// to lambda. Faster algorithms exist but are more complex.
|
||||
int32 RandPoisson(float lambda, struct RandomState* state) {
|
||||
// Knuth's algorithm.
|
||||
KALDI_ASSERT(lambda >= 0);
|
||||
float L = expf(-lambda), p = 1.0;
|
||||
int32 k = 0;
|
||||
do {
|
||||
k++;
|
||||
float u = RandUniform(state);
|
||||
p *= u;
|
||||
} while (p > L);
|
||||
return k-1;
|
||||
}
|
||||
|
||||
void RandGauss2(float *a, float *b, RandomState *state) {
|
||||
KALDI_ASSERT(a);
|
||||
KALDI_ASSERT(b);
|
||||
float u1 = RandUniform(state);
|
||||
float u2 = RandUniform(state);
|
||||
u1 = sqrtf(-2.0f * logf(u1));
|
||||
u2 = 2.0f * M_PI * u2;
|
||||
*a = u1 * cosf(u2);
|
||||
*b = u1 * sinf(u2);
|
||||
}
|
||||
|
||||
void RandGauss2(double *a, double *b, RandomState *state) {
|
||||
KALDI_ASSERT(a);
|
||||
KALDI_ASSERT(b);
|
||||
float a_float, b_float;
|
||||
// Just because we're using doubles doesn't mean we need super-high-quality
|
||||
// random numbers, so we just use the floating-point version internally.
|
||||
RandGauss2(&a_float, &b_float, state);
|
||||
*a = a_float;
|
||||
*b = b_float;
|
||||
}
|
||||
|
||||
|
||||
} // end namespace kaldi
|
||||
@@ -0,0 +1,363 @@
|
||||
// base/kaldi-math.h
|
||||
|
||||
// Copyright 2009-2011 Ondrej Glembek; Microsoft Corporation; Yanmin Qian;
|
||||
// Jan Silovsky; Saarland University
|
||||
//
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef KALDI_BASE_KALDI_MATH_H_
|
||||
#define KALDI_BASE_KALDI_MATH_H_ 1
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <float.h>
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "base/kaldi-types.h"
|
||||
#include "base/kaldi-common.h"
|
||||
|
||||
|
||||
#ifndef DBL_EPSILON
|
||||
#define DBL_EPSILON 2.2204460492503131e-16
|
||||
#endif
|
||||
#ifndef FLT_EPSILON
|
||||
#define FLT_EPSILON 1.19209290e-7f
|
||||
#endif
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
#endif
|
||||
|
||||
#ifndef M_SQRT2
|
||||
#define M_SQRT2 1.4142135623730950488016887
|
||||
#endif
|
||||
|
||||
#ifndef M_2PI
|
||||
#define M_2PI 6.283185307179586476925286766559005
|
||||
#endif
|
||||
|
||||
#ifndef M_SQRT1_2
|
||||
#define M_SQRT1_2 0.7071067811865475244008443621048490
|
||||
#endif
|
||||
|
||||
#ifndef M_LOG_2PI
|
||||
#define M_LOG_2PI 1.8378770664093454835606594728112
|
||||
#endif
|
||||
|
||||
#ifndef M_LN2
|
||||
#define M_LN2 0.693147180559945309417232121458
|
||||
#endif
|
||||
|
||||
#ifndef M_LN10
|
||||
#define M_LN10 2.302585092994045684017991454684
|
||||
#endif
|
||||
|
||||
|
||||
#define KALDI_ISNAN std::isnan
|
||||
#define KALDI_ISINF std::isinf
|
||||
#define KALDI_ISFINITE(x) std::isfinite(x)
|
||||
|
||||
#if !defined(KALDI_SQR)
|
||||
# define KALDI_SQR(x) ((x) * (x))
|
||||
#endif
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
inline double Exp(double x) { return exp(x); }
|
||||
#ifndef KALDI_NO_EXPF
|
||||
inline float Exp(float x) { return expf(x); }
|
||||
#else
|
||||
inline float Exp(float x) { return exp(static_cast<double>(x)); }
|
||||
#endif // KALDI_NO_EXPF
|
||||
#else
|
||||
inline double Exp(double x) { return exp(x); }
|
||||
#if !defined(__INTEL_COMPILER) && _MSC_VER == 1800 && defined(_M_X64)
|
||||
// Microsoft CL v18.0 buggy 64-bit implementation of
|
||||
// expf() incorrectly returns -inf for exp(-inf).
|
||||
inline float Exp(float x) { return exp(static_cast<double>(x)); }
|
||||
#else
|
||||
inline float Exp(float x) { return expf(x); }
|
||||
#endif // !defined(__INTEL_COMPILER) && _MSC_VER == 1800 && defined(_M_X64)
|
||||
#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
|
||||
inline double Log(double x) { return log(x); }
|
||||
inline float Log(float x) { return logf(x); }
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER >= 1700)
|
||||
inline double Log1p(double x) { return log1p(x); }
|
||||
inline float Log1p(float x) { return log1pf(x); }
|
||||
#else
|
||||
inline double Log1p(double x) {
|
||||
const double cutoff = 1.0e-08;
|
||||
if (x < cutoff)
|
||||
return x - 0.5 * x * x;
|
||||
else
|
||||
return Log(1.0 + x);
|
||||
}
|
||||
|
||||
inline float Log1p(float x) {
|
||||
const float cutoff = 1.0e-07;
|
||||
if (x < cutoff)
|
||||
return x - 0.5 * x * x;
|
||||
else
|
||||
return Log(1.0 + x);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const double kMinLogDiffDouble = Log(DBL_EPSILON); // negative!
|
||||
static const float kMinLogDiffFloat = Log(FLT_EPSILON); // negative!
|
||||
|
||||
// -infinity
|
||||
const float kLogZeroFloat = -std::numeric_limits<float>::infinity();
|
||||
const double kLogZeroDouble = -std::numeric_limits<double>::infinity();
|
||||
const BaseFloat kLogZeroBaseFloat = -std::numeric_limits<BaseFloat>::infinity();
|
||||
|
||||
// Returns a random integer between 0 and RAND_MAX, inclusive
|
||||
int Rand(struct RandomState* state = NULL);
|
||||
|
||||
// State for thread-safe random number generator
|
||||
struct RandomState {
|
||||
RandomState();
|
||||
unsigned seed;
|
||||
};
|
||||
|
||||
// Returns a random integer between first and last inclusive.
|
||||
int32 RandInt(int32 first, int32 last, struct RandomState* state = NULL);
|
||||
|
||||
// Returns true with probability "prob",
|
||||
bool WithProb(BaseFloat prob, struct RandomState* state = NULL);
|
||||
// with 0 <= prob <= 1 [we check this].
|
||||
// Internally calls Rand(). This function is carefully implemented so
|
||||
// that it should work even if prob is very small.
|
||||
|
||||
/// Returns a random number strictly between 0 and 1.
|
||||
inline float RandUniform(struct RandomState* state = NULL) {
|
||||
return static_cast<float>((Rand(state) + 1.0) / (RAND_MAX+2.0));
|
||||
}
|
||||
|
||||
inline float RandGauss(struct RandomState* state = NULL) {
|
||||
return static_cast<float>(sqrtf (-2 * Log(RandUniform(state)))
|
||||
* cosf(2*M_PI*RandUniform(state)));
|
||||
}
|
||||
|
||||
// Returns poisson-distributed random number. Uses Knuth's algorithm.
|
||||
// Take care: this takes time proportional
|
||||
// to lambda. Faster algorithms exist but are more complex.
|
||||
int32 RandPoisson(float lambda, struct RandomState* state = NULL);
|
||||
|
||||
// Returns a pair of gaussian random numbers. Uses Box-Muller transform
|
||||
void RandGauss2(float *a, float *b, RandomState *state = NULL);
|
||||
void RandGauss2(double *a, double *b, RandomState *state = NULL);
|
||||
|
||||
// Also see Vector<float,double>::RandCategorical().
|
||||
|
||||
// This is a randomized pruning mechanism that preserves expectations,
|
||||
// that we typically use to prune posteriors.
|
||||
template<class Float>
|
||||
inline Float RandPrune(Float post, BaseFloat prune_thresh,
|
||||
struct RandomState* state = NULL) {
|
||||
KALDI_ASSERT(prune_thresh >= 0.0);
|
||||
if (post == 0.0 || std::abs(post) >= prune_thresh)
|
||||
return post;
|
||||
return (post >= 0 ? 1.0 : -1.0) *
|
||||
(RandUniform(state) <= fabs(post)/prune_thresh ? prune_thresh : 0.0);
|
||||
}
|
||||
|
||||
// returns log(exp(x) + exp(y)).
|
||||
inline double LogAdd(double x, double y) {
|
||||
double diff;
|
||||
|
||||
if (x < y) {
|
||||
diff = x - y;
|
||||
x = y;
|
||||
} else {
|
||||
diff = y - x;
|
||||
}
|
||||
// diff is negative. x is now the larger one.
|
||||
|
||||
if (diff >= kMinLogDiffDouble) {
|
||||
double res;
|
||||
res = x + Log1p(Exp(diff));
|
||||
return res;
|
||||
} else {
|
||||
return x; // return the larger one.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// returns log(exp(x) + exp(y)).
|
||||
inline float LogAdd(float x, float y) {
|
||||
float diff;
|
||||
|
||||
if (x < y) {
|
||||
diff = x - y;
|
||||
x = y;
|
||||
} else {
|
||||
diff = y - x;
|
||||
}
|
||||
// diff is negative. x is now the larger one.
|
||||
|
||||
if (diff >= kMinLogDiffFloat) {
|
||||
float res;
|
||||
res = x + Log1p(Exp(diff));
|
||||
return res;
|
||||
} else {
|
||||
return x; // return the larger one.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// returns log(exp(x) - exp(y)).
|
||||
inline double LogSub(double x, double y) {
|
||||
if (y >= x) { // Throws exception if y>=x.
|
||||
if (y == x)
|
||||
return kLogZeroDouble;
|
||||
else
|
||||
KALDI_ERR << "Cannot subtract a larger from a smaller number.";
|
||||
}
|
||||
|
||||
double diff = y - x; // Will be negative.
|
||||
double res = x + Log(1.0 - Exp(diff));
|
||||
|
||||
// res might be NAN if diff ~0.0, and 1.0-exp(diff) == 0 to machine precision
|
||||
if (KALDI_ISNAN(res))
|
||||
return kLogZeroDouble;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// returns log(exp(x) - exp(y)).
|
||||
inline float LogSub(float x, float y) {
|
||||
if (y >= x) { // Throws exception if y>=x.
|
||||
if (y == x)
|
||||
return kLogZeroDouble;
|
||||
else
|
||||
KALDI_ERR << "Cannot subtract a larger from a smaller number.";
|
||||
}
|
||||
|
||||
float diff = y - x; // Will be negative.
|
||||
float res = x + Log(1.0f - Exp(diff));
|
||||
|
||||
// res might be NAN if diff ~0.0, and 1.0-exp(diff) == 0 to machine precision
|
||||
if (KALDI_ISNAN(res))
|
||||
return kLogZeroFloat;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// return abs(a - b) <= relative_tolerance * (abs(a)+abs(b)).
|
||||
static inline bool ApproxEqual(float a, float b,
|
||||
float relative_tolerance = 0.001) {
|
||||
// a==b handles infinities.
|
||||
if (a == b) return true;
|
||||
float diff = std::abs(a-b);
|
||||
if (diff == std::numeric_limits<float>::infinity()
|
||||
|| diff != diff) return false; // diff is +inf or nan.
|
||||
return (diff <= relative_tolerance*(std::abs(a)+std::abs(b)));
|
||||
}
|
||||
|
||||
/// assert abs(a - b) <= relative_tolerance * (abs(a)+abs(b))
|
||||
static inline void AssertEqual(float a, float b,
|
||||
float relative_tolerance = 0.001) {
|
||||
// a==b handles infinities.
|
||||
KALDI_ASSERT(ApproxEqual(a, b, relative_tolerance));
|
||||
}
|
||||
|
||||
|
||||
// RoundUpToNearestPowerOfTwo does the obvious thing. It crashes if n <= 0.
|
||||
int32 RoundUpToNearestPowerOfTwo(int32 n);
|
||||
|
||||
/// Returns a / b, rounding towards negative infinity in all cases.
|
||||
static inline int32 DivideRoundingDown(int32 a, int32 b) {
|
||||
KALDI_ASSERT(b != 0);
|
||||
if (a * b >= 0)
|
||||
return a / b;
|
||||
else if (a < 0)
|
||||
return (a - b + 1) / b;
|
||||
else
|
||||
return (a - b - 1) / b;
|
||||
}
|
||||
|
||||
template<class I> I Gcd(I m, I n) {
|
||||
if (m == 0 || n == 0) {
|
||||
if (m == 0 && n == 0) { // gcd not defined, as all integers are divisors.
|
||||
KALDI_ERR << "Undefined GCD since m = 0, n = 0.";
|
||||
}
|
||||
return (m == 0 ? (n > 0 ? n : -n) : ( m > 0 ? m : -m));
|
||||
// return absolute value of whichever is nonzero
|
||||
}
|
||||
// could use compile-time assertion
|
||||
// but involves messing with complex template stuff.
|
||||
KALDI_ASSERT(std::numeric_limits<I>::is_integer);
|
||||
while (1) {
|
||||
m %= n;
|
||||
if (m == 0) return (n > 0 ? n : -n);
|
||||
n %= m;
|
||||
if (n == 0) return (m > 0 ? m : -m);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the least common multiple of two integers. Will
|
||||
/// crash unless the inputs are positive.
|
||||
template<class I> I Lcm(I m, I n) {
|
||||
KALDI_ASSERT(m > 0 && n > 0);
|
||||
I gcd = Gcd(m, n);
|
||||
return gcd * (m/gcd) * (n/gcd);
|
||||
}
|
||||
|
||||
|
||||
template<class I> void Factorize(I m, std::vector<I> *factors) {
|
||||
// Splits a number into its prime factors, in sorted order from
|
||||
// least to greatest, with duplication. A very inefficient
|
||||
// algorithm, which is mainly intended for use in the
|
||||
// mixed-radix FFT computation (where we assume most factors
|
||||
// are small).
|
||||
KALDI_ASSERT(factors != NULL);
|
||||
KALDI_ASSERT(m >= 1); // Doesn't work for zero or negative numbers.
|
||||
factors->clear();
|
||||
I small_factors[10] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
|
||||
|
||||
// First try small factors.
|
||||
for (I i = 0; i < 10; i++) {
|
||||
if (m == 1) return; // We're done.
|
||||
while (m % small_factors[i] == 0) {
|
||||
m /= small_factors[i];
|
||||
factors->push_back(small_factors[i]);
|
||||
}
|
||||
}
|
||||
// Next try all odd numbers starting from 31.
|
||||
for (I j = 31;; j += 2) {
|
||||
if (m == 1) return;
|
||||
while (m % j == 0) {
|
||||
m /= j;
|
||||
factors->push_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline double Hypot(double x, double y) { return hypot(x, y); }
|
||||
inline float Hypot(float x, float y) { return hypotf(x, y); }
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace kaldi
|
||||
|
||||
|
||||
#endif // KALDI_BASE_KALDI_MATH_H_
|
||||
@@ -0,0 +1,76 @@
|
||||
// base/kaldi-types.h
|
||||
|
||||
// Copyright 2009-2011 Microsoft Corporation; Saarland University;
|
||||
// Jan Silovsky; Yanmin Qian
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef KALDI_BASE_KALDI_TYPES_H_
|
||||
#define KALDI_BASE_KALDI_TYPES_H_ 1
|
||||
|
||||
namespace kaldi {
|
||||
// TYPEDEFS ..................................................................
|
||||
#if (KALDI_DOUBLEPRECISION != 0)
|
||||
typedef double BaseFloat;
|
||||
#else
|
||||
typedef float BaseFloat;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <basetsd.h>
|
||||
// Don't define ssize_t as macro - let fst/types.h handle it
|
||||
// #define ssize_t SSIZE_T // This causes conflicts with fst/types.h
|
||||
#endif
|
||||
|
||||
// we can do this a different way if some platform
|
||||
// we find in the future lacks stdint.h
|
||||
#include <stdint.h>
|
||||
|
||||
// for discussion on what to do if you need compile kaldi
|
||||
// without OpenFST, see the bottom of this this file
|
||||
#include <fst/types.h>
|
||||
|
||||
namespace kaldi {
|
||||
using ::int16;
|
||||
using ::int32;
|
||||
using ::int64;
|
||||
using ::uint16;
|
||||
using ::uint32;
|
||||
using ::uint64;
|
||||
typedef float float32;
|
||||
typedef double double64;
|
||||
} // end namespace kaldi
|
||||
|
||||
// In a theoretical case you decide compile Kaldi without the OpenFST
|
||||
// comment the previous namespace statement and uncomment the following
|
||||
/*
|
||||
namespace kaldi {
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
typedef int64_t int64;
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
typedef float float32;
|
||||
typedef double double64;
|
||||
} // end namespace kaldi
|
||||
*/
|
||||
|
||||
#endif // KALDI_BASE_KALDI_TYPES_H_
|
||||
@@ -0,0 +1,46 @@
|
||||
// base/kaldi-utils.cc
|
||||
// Copyright 2009-2011 Karel Vesely; Yanmin Qian; Microsoft Corporation
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "base/kaldi-utils.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
std::string CharToString(const char &c) {
|
||||
char buf[20];
|
||||
if (std::isprint(c))
|
||||
std::snprintf(buf, sizeof(buf), "\'%c\'", c);
|
||||
else
|
||||
std::snprintf(buf, sizeof(buf), "[character %d]", static_cast<int>(c));
|
||||
return buf;
|
||||
}
|
||||
|
||||
void Sleep(double sec) {
|
||||
// duration_cast<> rounds down, add 0.5 to compensate.
|
||||
auto dur_nanos = std::chrono::duration<double, std::nano>(sec * 1E9 + 0.5);
|
||||
auto dur_syshires = std::chrono::duration_cast<
|
||||
typename std::chrono::high_resolution_clock::duration>(dur_nanos);
|
||||
std::this_thread::sleep_for(dur_syshires);
|
||||
}
|
||||
|
||||
} // end namespace kaldi
|
||||
@@ -0,0 +1,162 @@
|
||||
// base/kaldi-utils.h
|
||||
|
||||
// Copyright 2009-2011 Ondrej Glembek; Microsoft Corporation;
|
||||
// Saarland University; Karel Vesely; Yanmin Qian
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef KALDI_BASE_KALDI_UTILS_H_
|
||||
#define KALDI_BASE_KALDI_UTILS_H_ 1
|
||||
|
||||
#if _MSC_VER
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <stdio.h>
|
||||
#define unlink _unlink
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable: 4244 4056 4305 4800 4267 4996 4756 4661)
|
||||
#if _MSC_VER < 1400
|
||||
#define __restrict__
|
||||
#else
|
||||
#define __restrict__ __restrict
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define KALDI_MEMALIGN(align, size, pp_orig) \
|
||||
(*(pp_orig) = _aligned_malloc(size, align))
|
||||
# define KALDI_MEMALIGN_FREE(x) _aligned_free(x)
|
||||
#elif defined(__CYGWIN__)
|
||||
# define KALDI_MEMALIGN(align, size, pp_orig) \
|
||||
(*(pp_orig) = aligned_alloc(align, size))
|
||||
# define KALDI_MEMALIGN_FREE(x) free(x)
|
||||
#else
|
||||
# define KALDI_MEMALIGN(align, size, pp_orig) \
|
||||
(!posix_memalign(pp_orig, align, size) ? *(pp_orig) : NULL)
|
||||
# define KALDI_MEMALIGN_FREE(x) free(x)
|
||||
#endif
|
||||
|
||||
#ifdef __ICC
|
||||
#pragma warning(disable: 383) // ICPC remark we don't want.
|
||||
#pragma warning(disable: 810) // ICPC remark we don't want.
|
||||
#pragma warning(disable: 981) // ICPC remark we don't want.
|
||||
#pragma warning(disable: 1418) // ICPC remark we don't want.
|
||||
#pragma warning(disable: 444) // ICPC remark we don't want.
|
||||
#pragma warning(disable: 869) // ICPC remark we don't want.
|
||||
#pragma warning(disable: 1287) // ICPC remark we don't want.
|
||||
#pragma warning(disable: 279) // ICPC remark we don't want.
|
||||
#pragma warning(disable: 981) // ICPC remark we don't want.
|
||||
#endif
|
||||
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
|
||||
// CharToString prints the character in a human-readable form, for debugging.
|
||||
std::string CharToString(const char &c);
|
||||
|
||||
|
||||
inline int MachineIsLittleEndian() {
|
||||
int check = 1;
|
||||
return (*reinterpret_cast<char*>(&check) != 0);
|
||||
}
|
||||
|
||||
// This function kaldi::Sleep() provides a portable way
|
||||
// to sleep for a possibly fractional
|
||||
// number of seconds. On Windows it's only accurate to microseconds.
|
||||
void Sleep(double seconds);
|
||||
}
|
||||
|
||||
#define KALDI_SWAP8(a) do { \
|
||||
int t = (reinterpret_cast<char*>(&a))[0];\
|
||||
(reinterpret_cast<char*>(&a))[0]=(reinterpret_cast<char*>(&a))[7];\
|
||||
(reinterpret_cast<char*>(&a))[7]=t;\
|
||||
t = (reinterpret_cast<char*>(&a))[1];\
|
||||
(reinterpret_cast<char*>(&a))[1]=(reinterpret_cast<char*>(&a))[6];\
|
||||
(reinterpret_cast<char*>(&a))[6]=t;\
|
||||
t = (reinterpret_cast<char*>(&a))[2];\
|
||||
(reinterpret_cast<char*>(&a))[2]=(reinterpret_cast<char*>(&a))[5];\
|
||||
(reinterpret_cast<char*>(&a))[5]=t;\
|
||||
t = (reinterpret_cast<char*>(&a))[3];\
|
||||
(reinterpret_cast<char*>(&a))[3]=(reinterpret_cast<char*>(&a))[4];\
|
||||
(reinterpret_cast<char*>(&a))[4]=t;} while (0)
|
||||
#define KALDI_SWAP4(a) do { \
|
||||
int t = (reinterpret_cast<char*>(&a))[0];\
|
||||
(reinterpret_cast<char*>(&a))[0]=(reinterpret_cast<char*>(&a))[3];\
|
||||
(reinterpret_cast<char*>(&a))[3]=t;\
|
||||
t = (reinterpret_cast<char*>(&a))[1];\
|
||||
(reinterpret_cast<char*>(&a))[1]=(reinterpret_cast<char*>(&a))[2];\
|
||||
(reinterpret_cast<char*>(&a))[2]=t;} while (0)
|
||||
#define KALDI_SWAP2(a) do { \
|
||||
int t = (reinterpret_cast<char*>(&a))[0];\
|
||||
(reinterpret_cast<char*>(&a))[0]=(reinterpret_cast<char*>(&a))[1];\
|
||||
(reinterpret_cast<char*>(&a))[1]=t;} while (0)
|
||||
|
||||
|
||||
///\brief Declare deleted copy constructor and copy assignment operator=().
|
||||
///
|
||||
/// Use this macro in the \e public part of a class declaration in a header
|
||||
/// file, next to its constructors, so that uncopyability of the type is
|
||||
/// clearly readable. Place a semicolon after it.
|
||||
///\param type The exact enclosing class name.
|
||||
#define KALDI_DISALLOW_COPY_AND_ASSIGN(type) \
|
||||
type(const type&) = delete; \
|
||||
type& operator=(const type&) = delete
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
#define KALDI_COMPILE_TIME_ASSERT static_assert
|
||||
#else
|
||||
#define KALDI_COMPILE_TIME_ASSERT(b) static_assert((b), #b)
|
||||
#endif
|
||||
|
||||
#define KALDI_ASSERT_IS_INTEGER_TYPE(I) \
|
||||
KALDI_COMPILE_TIME_ASSERT(std::numeric_limits<I>::is_specialized \
|
||||
&& std::numeric_limits<I>::is_integer)
|
||||
|
||||
#define KALDI_ASSERT_IS_FLOATING_TYPE(F) \
|
||||
KALDI_COMPILE_TIME_ASSERT(std::numeric_limits<F>::is_specialized \
|
||||
&& !std::numeric_limits<F>::is_integer)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define KALDI_STRCASECMP _stricmp
|
||||
#elif defined(__CYGWIN__)
|
||||
#include <strings.h>
|
||||
#define KALDI_STRCASECMP strcasecmp
|
||||
#else
|
||||
#define KALDI_STRCASECMP strcasecmp
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
# define KALDI_STRTOLL(cur_cstr, end_cstr) _strtoi64(cur_cstr, end_cstr, 10);
|
||||
#else
|
||||
# define KALDI_STRTOLL(cur_cstr, end_cstr) strtoll(cur_cstr, end_cstr, 10);
|
||||
#endif
|
||||
|
||||
#endif // KALDI_BASE_KALDI_UTILS_H_
|
||||
@@ -0,0 +1,45 @@
|
||||
// base/timer-test.cc
|
||||
|
||||
// Copyright 2009-2011 Microsoft Corporation
|
||||
// 2014 Johns Hopkins University (author: Daniel Povey)
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "base/timer.h"
|
||||
#include "base/kaldi-common.h"
|
||||
#include "base/kaldi-utils.h"
|
||||
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
void TimerTest() {
|
||||
float time_secs = 0.025 * (rand() % 10);
|
||||
std::cout << "target is " << time_secs << "\n";
|
||||
Timer timer;
|
||||
Sleep(time_secs);
|
||||
BaseFloat f = timer.Elapsed();
|
||||
std::cout << "time is " << f << std::endl;
|
||||
if (fabs(time_secs - f) > 0.05)
|
||||
KALDI_ERR << "Timer fail: waited " << f << " seconds instead of "
|
||||
<< time_secs << " secs.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
for (int i = 0; i < 4; i++)
|
||||
kaldi::TimerTest();
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// base/timer.cc
|
||||
|
||||
// Copyright 2018 Johns Hopkins University (author: Daniel Povey)
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "base/timer.h"
|
||||
#include "base/kaldi-error.h"
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace kaldi {
|
||||
|
||||
class ProfileStats {
|
||||
public:
|
||||
void AccStats(const char *function_name, double elapsed) {
|
||||
std::unordered_map<const char*, ProfileStatsEntry>::iterator
|
||||
iter = map_.find(function_name);
|
||||
if (iter == map_.end()) {
|
||||
map_[function_name] = ProfileStatsEntry(function_name);
|
||||
map_[function_name].total_time = elapsed;
|
||||
} else {
|
||||
iter->second.total_time += elapsed;
|
||||
}
|
||||
}
|
||||
~ProfileStats() {
|
||||
// This map makes sure we agglomerate the time if there were any duplicate
|
||||
// addresses of strings.
|
||||
std::unordered_map<std::string, double> total_time;
|
||||
for (auto iter = map_.begin(); iter != map_.end(); iter++)
|
||||
total_time[iter->second.name] += iter->second.total_time;
|
||||
|
||||
ReverseSecondComparator comp;
|
||||
std::vector<std::pair<std::string, double> > pairs(total_time.begin(),
|
||||
total_time.end());
|
||||
std::sort(pairs.begin(), pairs.end(), comp);
|
||||
for (size_t i = 0; i < pairs.size(); i++) {
|
||||
KALDI_LOG << "Time taken in " << pairs[i].first << " is "
|
||||
<< std::fixed << std::setprecision(2) << pairs[i].second << "s.";
|
||||
}
|
||||
}
|
||||
private:
|
||||
|
||||
struct ProfileStatsEntry {
|
||||
std::string name;
|
||||
double total_time;
|
||||
ProfileStatsEntry() { }
|
||||
ProfileStatsEntry(const char *name): name(name) { }
|
||||
};
|
||||
|
||||
struct ReverseSecondComparator {
|
||||
bool operator () (const std::pair<std::string, double> &a,
|
||||
const std::pair<std::string, double> &b) {
|
||||
return a.second > b.second;
|
||||
}
|
||||
};
|
||||
|
||||
// Note: this map is keyed on the address of the string, there is no proper
|
||||
// hash function. The assumption is that the strings are compile-time
|
||||
// constants.
|
||||
std::unordered_map<const char*, ProfileStatsEntry> map_;
|
||||
};
|
||||
|
||||
ProfileStats g_profile_stats;
|
||||
|
||||
Profiler::~Profiler() {
|
||||
g_profile_stats.AccStats(name_, tim_.Elapsed());
|
||||
}
|
||||
|
||||
} // namespace kaldi
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
// base/timer.h
|
||||
|
||||
// Copyright 2009-2011 Ondrej Glembek; Microsoft Corporation
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// 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
|
||||
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef KALDI_BASE_TIMER_H_
|
||||
#define KALDI_BASE_TIMER_H_
|
||||
|
||||
#include "base/kaldi-utils.h"
|
||||
#include "base/kaldi-error.h"
|
||||
|
||||
|
||||
#if defined(_MSC_VER) || defined(MINGW)
|
||||
|
||||
namespace kaldi {
|
||||
class Timer {
|
||||
public:
|
||||
Timer() { Reset(); }
|
||||
|
||||
// You can initialize with bool to control whether or not you want the time to
|
||||
// be set when the object is created.
|
||||
explicit Timer(bool set_timer) { if (set_timer) Reset(); }
|
||||
|
||||
void Reset() {
|
||||
QueryPerformanceCounter(&time_start_);
|
||||
}
|
||||
double Elapsed() const {
|
||||
LARGE_INTEGER time_end;
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceCounter(&time_end);
|
||||
|
||||
if (QueryPerformanceFrequency(&freq) == 0) {
|
||||
// Hardware does not support this.
|
||||
return 0.0;
|
||||
}
|
||||
return (static_cast<double>(time_end.QuadPart) -
|
||||
static_cast<double>(time_start_.QuadPart)) /
|
||||
(static_cast<double>(freq.QuadPart));
|
||||
}
|
||||
private:
|
||||
LARGE_INTEGER time_start_;
|
||||
};
|
||||
|
||||
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace kaldi {
|
||||
class Timer {
|
||||
public:
|
||||
Timer() { Reset(); }
|
||||
|
||||
// You can initialize with bool to control whether or not you want the time to
|
||||
// be set when the object is created.
|
||||
explicit Timer(bool set_timer) { if (set_timer) Reset(); }
|
||||
|
||||
void Reset() { gettimeofday(&this->time_start_, &time_zone_); }
|
||||
|
||||
/// Returns time in seconds.
|
||||
double Elapsed() const {
|
||||
struct timeval time_end;
|
||||
struct timezone time_zone;
|
||||
gettimeofday(&time_end, &time_zone);
|
||||
double t1, t2;
|
||||
t1 = static_cast<double>(time_start_.tv_sec) +
|
||||
static_cast<double>(time_start_.tv_usec)/(1000*1000);
|
||||
t2 = static_cast<double>(time_end.tv_sec) +
|
||||
static_cast<double>(time_end.tv_usec)/(1000*1000);
|
||||
return t2-t1;
|
||||
}
|
||||
|
||||
private:
|
||||
struct timeval time_start_;
|
||||
struct timezone time_zone_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class Profiler {
|
||||
public:
|
||||
// Caution: the 'const char' should always be a string constant; for speed,
|
||||
// internally the profiling code uses the address of it as a lookup key.
|
||||
Profiler(const char *function_name): name_(function_name) { }
|
||||
~Profiler();
|
||||
private:
|
||||
Timer tim_;
|
||||
const char *name_;
|
||||
};
|
||||
|
||||
// To add timing info for a function, you just put
|
||||
// KALDI_PROFILE;
|
||||
// at the beginning of the function. Caution: this doesn't
|
||||
// include the class name.
|
||||
#define KALDI_PROFILE Profiler _profiler(__func__)
|
||||
|
||||
|
||||
|
||||
} // namespace kaldi
|
||||
|
||||
|
||||
#endif // KALDI_BASE_TIMER_H_
|
||||
Reference in New Issue
Block a user