<commit_before>// c++ -std=c++11 converter.cpp -o converter -I/usr/local/include -L/usr/local/lib -lassimp -g -Wall
/// When debugging, link against my assimp library:
// c++ -std=c++11 converter.cpp -o converter -I/usr/local/include -L/Users/yotam/Work/ext/assimp/build/code -lassimpd -g -Wall

#define SAVE_RIG 1

#include <iostream>
#include <fstream>
#include <algorithm> // pair
#include <unordered_map>
#include <cassert>

#include <assimp/Exporter.hpp>
#include <assimp/cimport.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

namespace
{
/// From my: stl.h

// Behaves like the python os.path.split() function.
inline std::pair< std::string, std::string > os_path_split( const std::string& path )
{
    const std::string::size_type split = path.find_last_of( "/" );
    if( split == std::string::npos )
        return std::make_pair( std::string(), path );
    else
    {
        std::string::size_type split_start = split;
        // Remove multiple trailing slashes.
        while( split_start > 0 && path[ split_start-1 ] == '/' ) split_start -= 1;
        // Don't remove the leading slash.
        if( split_start == 0 ) split_start = 1;
        return std::make_pair( path.substr( 0, split_start ), path.substr( split+1 ) );
    }
}

// Behaves like the python os.path.splitext() function.
inline std::pair< std::string, std::string > os_path_splitext( const std::string& path )
{
    const std::string::size_type split_dot = path.find_last_of( "." );
    const std::string::size_type split_slash = path.find_last_of( "/" );
    if( split_dot != std::string::npos && (split_slash == std::string::npos || split_slash < split_dot) )
        return std::make_pair( path.substr( 0, split_dot ), path.substr( split_dot ) );
    else
        return std::make_pair( path, std::string() );
}

/// From: http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c
bool os_path_exists( const std::string& name ) {
    std::ifstream f(name.c_str());
    if (f.good()) {
        f.close();
        return true;
    } else {
        f.close();
        return false;
    }
}
}

void print_importers( std::ostream& out )
{
    out << "## Importers:\n";

    aiString s;
    aiGetExtensionList( &s );

    out << s.C_Str() << std::endl;
}

void print_exporters( std::ostream& out )
{
    out << "## Exporters:\n";

    Assimp::Exporter exporter;
    const size_t count = exporter.GetExportFormatCount();

    for( size_t i = 0; i < count; ++i )
    {
        const aiExportFormatDesc* desc = exporter.GetExportFormatDescription( i );
        assert( desc );

        out << desc->fileExtension << ": " << desc->description << " (id: " << desc->id << ")" << std::endl;
    }
}

const char* IdFromExtension( const std::string& extension )
{
    Assimp::Exporter exporter;
    const size_t count = exporter.GetExportFormatCount();

    for( size_t i = 0; i < count; ++i )
    {
        const aiExportFormatDesc* desc = exporter.GetExportFormatDescription( i );
        assert( desc );

        if( extension == desc->fileExtension )
        {
            std::cout << "Found a match for extension: " << extension << std::endl;
            std::cout << desc->fileExtension << ": " << desc->description << " (id: " << desc->id << ")" << std::endl;

            return desc->id;
        }
    }

    return nullptr;
}

#if SAVE_RIG

typedef std::unordered_map< std::string, aiMatrix4x4 > StringToMatrix4x4;
void print( const aiMatrix4x4& matrix ) {
    std::cout << ' ' << matrix.a1 << ' ' << matrix.a2 << ' ' << matrix.a3 << ' ' << matrix.a4 << '\n';
    std::cout << ' ' << matrix.b1 << ' ' << matrix.b2 << ' ' << matrix.b3 << ' ' << matrix.b4 << '\n';
    std::cout << ' ' << matrix.c1 << ' ' << matrix.c2 << ' ' << matrix.c3 << ' ' << matrix.c4 << '\n';
    std::cout << ' ' << matrix.d1 << ' ' << matrix.d2 << ' ' << matrix.d3 << ' ' << matrix.d4 << '\n';
}
void print( const StringToMatrix4x4& name2matrix ) {
    for( const auto& iter : name2matrix ) {
        std::cout << iter.first << ":\n";

        print( iter.second );
    }
}

void save_rig( const aiMesh* mesh )
{
    assert( mesh );

    // Iterate over the bones of the mesh.
    for( int bone_index = 0; bone_index < mesh->mNumBones; ++bone_index ) {
        const aiBone* bone = mesh->mBones[ bone_index ];

        // Save the vertex weights for the bone.
        // Lookup the index for the bone by its name.

        std::cout << "bone.offset for bone.name: " << bone->mName.C_Str() << std::endl;
        print( bone->mOffsetMatrix );
    }
}

aiMatrix4x4 FilterNodeTransformation( const aiMatrix4x4& transformation ) {
    return transformation;

    // Decompose the transformation matrix into translation, rotation, and scaling.
    aiVector3D scaling, translation_vector;
    aiQuaternion rotation;
    transformation.Decompose( scaling, rotation, translation_vector );
    // rotation.w *= -1;
    rotation.Normalize();

    // Convert the translation into a matrix.
    aiMatrix4x4 translation_matrix;
    aiMatrix4x4::Translation( translation_vector, translation_matrix );

    // Keep the rotation times the translation.
    aiMatrix4x4 keep( aiMatrix4x4( rotation.GetMatrix() ) * translation_matrix );
    return keep;
}

// typedef std::unordered_map< std::string, std::string > StringToString;
void recurse( const aiNode* node, const aiMatrix4x4& parent_transformation, StringToMatrix4x4& name2transformation, StringToMatrix4x4& name2offset, StringToMatrix4x4& name2offset_skelmeshbuilder ) {
    assert( node );
    assert( name2transformation.find( node->mName.C_Str() ) == name2transformation.end() );
    assert( name2offset.find( node->mName.C_Str() ) == name2offset.end() );
    assert( name2offset_skelmeshbuilder.find( node->mName.C_Str() ) == name2offset_skelmeshbuilder.end() );

    const std::string name( node->mName.C_Str() );

    aiMatrix4x4 node_transformation = FilterNodeTransformation( node->mTransformation );
    // node_transformation.Transpose();
    const aiMatrix4x4 transformation_so_far = parent_transformation * node_transformation;

    name2transformation[ name ] = transformation_so_far;

    // Make a copy and invert it. That should be the offset matrix.
    aiMatrix4x4 offset = transformation_so_far;
    offset.Inverse();
    name2offset[ name ] = offset;

    // Calculate the offset matrix the way SkeletonMeshBuilder.cpp:179--182 does.
    {
        // calculate the bone offset matrix by concatenating the inverse transformations of all parents
        aiMatrix4x4 offset_skelmeshbuilder = aiMatrix4x4( node->mTransformation ).Inverse();
        for( aiNode* parent = node->mParent; parent != NULL; parent = parent->mParent ) {
            offset_skelmeshbuilder = offset_skelmeshbuilder * aiMatrix4x4( parent->mTransformation ).Inverse();
        }
        name2offset_skelmeshbuilder[ name ] = offset_skelmeshbuilder;
    }

    for( int child_index = 0; child_index < node->mNumChildren; ++child_index ) {
        recurse( node->mChildren[ child_index ], transformation_so_far, name2transformation, name2offset, name2offset_skelmeshbuilder );
    }
}

void save_rig( const aiScene* scene )
{
    printf( "# Saving the rig.\n" );

    assert( scene );
    assert( scene->mRootNode );

    StringToMatrix4x4 node2transformation, node2offset, name2offset_skelmeshbuilder;
    /// WHY WHY WHY WHY WHY
    const aiMatrix4x4 I( 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1 );
    recurse( scene->mRootNode, I, node2transformation, node2offset, name2offset_skelmeshbuilder );
    std::cout << "## name2transformation\n";
    print( node2transformation );
    std::cout << "## name2offset\n";
    print( node2offset );
    std::cout << "## name2offset the way SkeletonMeshBuilder does it\n";
    print( name2offset_skelmeshbuilder );

    /// 1 Find the immediate parent of each bone

    // TODO: Save the bones. There should be some kind of nodes with their
    //       names and positions. (We might also need the offsetmatrix from the bone itself).
    //       Store a map of name to index.
    //       See (maybe): http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html
    /*
import pyassimp
filename = '/Users/yotam/Work/ext/three.js/examples/models/collada/monster/monster.dae'
scene = pyassimp.load( filename )
spaces = 0
def recurse( node ):
    global spaces
    print (' '*spaces), node.name #, node.transformation
    spaces += 1
    for child in node.children: recurse( child )
    spaces -= 1

recurse( scene.rootnode )
    */

    // Meshes have bones. Iterate over meshes.
    for( int mesh_index = 0; mesh_index < scene->mNumMeshes; ++mesh_index ) {
        printf( "Mesh %d\n", mesh_index );
        save_rig( scene->mMeshes[ mesh_index ] );
    }
}
#endif

void usage( const char* argv0, std::ostream& out )
{
    out << "Usage: " << argv0 << " path/to/input path/to/output" << std::endl;

    out << std::endl;
    print_importers( out );

    out << std::endl;
    print_exporters( out );
}

int main( int argc, char* argv[] )
{
    /// We need three arguments: the program, the input path, and the output path.
    if( 3 != argc ) {
        usage( argv[0], std::cerr );
        return -1;
    }

    /// Store the input and output paths.
    const char* inpath = argv[1];
    const char* outpath = argv[2];

    // Exit if the output path already exists.
    if( os_path_exists( outpath ) ) {
        std::cerr << "ERROR: Output path exists. Not clobbering: " << outpath << std::endl;
        usage( argv[0], std::cerr );
        return -1;
    }

    /// Get the extension of the output path and its corresponding ASSIMP id.
    std::string extension = os_path_splitext( outpath ).second;
    // os_path_splitext.second returns an extension of the form ".obj".
    // We want the substring from position 1 to the end.
    if( extension.size() <= 1 ) {
        std::cerr << "ERROR: No extension detected on the output path: " << extension << std::endl;
        usage( argv[0], std::cerr );
        return -1;
    }
    extension = extension.substr(1);

    const char* exportId = IdFromExtension( extension );
    // Exit if we couldn't find a corresponding ASSIMP id.
    if( nullptr == exportId ) {
        std::cerr << "ERROR: Output extension unsupported: " << extension << std::endl;
        usage( argv[0], std::cerr );
        return -1;
    }

    /// Load the scene.
    const aiScene* scene = aiImportFile( inpath, 0 );
    if( nullptr == scene ) {
        std::cerr << "ERROR: " << aiGetErrorString() << std::endl;
    }
    std::cout << "Loaded: " << inpath << std::endl;

    /// Save the scene.
    const aiReturn result = aiExportScene( scene, exportId, outpath, 0 );
    if( aiReturn_SUCCESS != result ) {
        std::cerr << "ERROR: Could not save the scene: " << ( (aiReturn_OUTOFMEMORY == result) ? "Out of memory" : "Unknown reason" ) << std::endl;
    }
    std::cout << "Saved: " << outpath << std::endl;

#if SAVE_RIG
    /// Save the rig.
    save_rig( scene );
#endif

    // Cleanup.
    aiReleaseImport( scene );

    return 0;
}
<commit_msg>answered question about why MD5 needs a strange rotation matrix above the rootnode to match whats in the file<commit_after>// c++ -std=c++11 converter.cpp -o converter -I/usr/local/include -L/usr/local/lib -lassimp -g -Wall
/// When debugging, link against my assimp library:
// c++ -std=c++11 converter.cpp -o converter -I/usr/local/include -L/Users/yotam/Work/ext/assimp/build/code -lassimpd -g -Wall

#define SAVE_RIG 1

#include <iostream>
#include <fstream>
#include <algorithm> // pair
#include <unordered_map>
#include <cassert>

#include <assimp/Exporter.hpp>
#include <assimp/cimport.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

namespace
{
/// From my: stl.h

// Behaves like the python os.path.split() function.
inline std::pair< std::string, std::string > os_path_split( const std::string& path )
{
    const std::string::size_type split = path.find_last_of( "/" );
    if( split == std::string::npos )
        return std::make_pair( std::string(), path );
    else
    {
        std::string::size_type split_start = split;
        // Remove multiple trailing slashes.
        while( split_start > 0 && path[ split_start-1 ] == '/' ) split_start -= 1;
        // Don't remove the leading slash.
        if( split_start == 0 ) split_start = 1;
        return std::make_pair( path.substr( 0, split_start ), path.substr( split+1 ) );
    }
}

// Behaves like the python os.path.splitext() function.
inline std::pair< std::string, std::string > os_path_splitext( const std::string& path )
{
    const std::string::size_type split_dot = path.find_last_of( "." );
    const std::string::size_type split_slash = path.find_last_of( "/" );
    if( split_dot != std::string::npos && (split_slash == std::string::npos || split_slash < split_dot) )
        return std::make_pair( path.substr( 0, split_dot ), path.substr( split_dot ) );
    else
        return std::make_pair( path, std::string() );
}

/// From: http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c
bool os_path_exists( const std::string& name ) {
    std::ifstream f(name.c_str());
    if (f.good()) {
        f.close();
        return true;
    } else {
        f.close();
        return false;
    }
}
}

void print_importers( std::ostream& out )
{
    out << "## Importers:\n";

    aiString s;
    aiGetExtensionList( &s );

    out << s.C_Str() << std::endl;
}

void print_exporters( std::ostream& out )
{
    out << "## Exporters:\n";

    Assimp::Exporter exporter;
    const size_t count = exporter.GetExportFormatCount();

    for( size_t i = 0; i < count; ++i )
    {
        const aiExportFormatDesc* desc = exporter.GetExportFormatDescription( i );
        assert( desc );

        out << desc->fileExtension << ": " << desc->description << " (id: " << desc->id << ")" << std::endl;
    }
}

const char* IdFromExtension( const std::string& extension )
{
    Assimp::Exporter exporter;
    const size_t count = exporter.GetExportFormatCount();

    for( size_t i = 0; i < count; ++i )
    {
        const aiExportFormatDesc* desc = exporter.GetExportFormatDescription( i );
        assert( desc );

        if( extension == desc->fileExtension )
        {
            std::cout << "Found a match for extension: " << extension << std::endl;
            std::cout << desc->fileExtension << ": " << desc->description << " (id: " << desc->id << ")" << std::endl;

            return desc->id;
        }
    }

    return nullptr;
}

#if SAVE_RIG

typedef std::unordered_map< std::string, aiMatrix4x4 > StringToMatrix4x4;
void print( const aiMatrix4x4& matrix ) {
    std::cout << ' ' << matrix.a1 << ' ' << matrix.a2 << ' ' << matrix.a3 << ' ' << matrix.a4 << '\n';
    std::cout << ' ' << matrix.b1 << ' ' << matrix.b2 << ' ' << matrix.b3 << ' ' << matrix.b4 << '\n';
    std::cout << ' ' << matrix.c1 << ' ' << matrix.c2 << ' ' << matrix.c3 << ' ' << matrix.c4 << '\n';
    std::cout << ' ' << matrix.d1 << ' ' << matrix.d2 << ' ' << matrix.d3 << ' ' << matrix.d4 << '\n';
}
void print( const StringToMatrix4x4& name2matrix ) {
    for( const auto& iter : name2matrix ) {
        std::cout << iter.first << ":\n";

        print( iter.second );
    }
}

void save_rig( const aiMesh* mesh )
{
    assert( mesh );

    // Iterate over the bones of the mesh.
    for( int bone_index = 0; bone_index < mesh->mNumBones; ++bone_index ) {
        const aiBone* bone = mesh->mBones[ bone_index ];

        // Save the vertex weights for the bone.
        // Lookup the index for the bone by its name.

        std::cout << "bone.offset for bone.name: " << bone->mName.C_Str() << std::endl;
        print( bone->mOffsetMatrix );
    }
}

aiMatrix4x4 FilterNodeTransformation( const aiMatrix4x4& transformation ) {
    return transformation;

    // Decompose the transformation matrix into translation, rotation, and scaling.
    aiVector3D scaling, translation_vector;
    aiQuaternion rotation;
    transformation.Decompose( scaling, rotation, translation_vector );
    // rotation.w *= -1;
    rotation.Normalize();

    // Convert the translation into a matrix.
    aiMatrix4x4 translation_matrix;
    aiMatrix4x4::Translation( translation_vector, translation_matrix );

    // Keep the rotation times the translation.
    aiMatrix4x4 keep( aiMatrix4x4( rotation.GetMatrix() ) * translation_matrix );
    return keep;
}

// typedef std::unordered_map< std::string, std::string > StringToString;
void recurse( const aiNode* node, const aiMatrix4x4& parent_transformation, StringToMatrix4x4& name2transformation, StringToMatrix4x4& name2offset, StringToMatrix4x4& name2offset_skelmeshbuilder ) {
    assert( node );
    assert( name2transformation.find( node->mName.C_Str() ) == name2transformation.end() );
    assert( name2offset.find( node->mName.C_Str() ) == name2offset.end() );
    assert( name2offset_skelmeshbuilder.find( node->mName.C_Str() ) == name2offset_skelmeshbuilder.end() );

    const std::string name( node->mName.C_Str() );

    aiMatrix4x4 node_transformation = FilterNodeTransformation( node->mTransformation );
    // node_transformation.Transpose();
    const aiMatrix4x4 transformation_so_far = parent_transformation * node_transformation;

    name2transformation[ name ] = transformation_so_far;

    // Make a copy and invert it. That should be the offset matrix.
    aiMatrix4x4 offset = transformation_so_far;
    offset.Inverse();
    name2offset[ name ] = offset;

    // Calculate the offset matrix the way SkeletonMeshBuilder.cpp:179--182 does.
    {
        // calculate the bone offset matrix by concatenating the inverse transformations of all parents
        aiMatrix4x4 offset_skelmeshbuilder = aiMatrix4x4( node->mTransformation ).Inverse();
        for( aiNode* parent = node->mParent; parent != NULL; parent = parent->mParent ) {
            offset_skelmeshbuilder = offset_skelmeshbuilder * aiMatrix4x4( parent->mTransformation ).Inverse();
        }
        name2offset_skelmeshbuilder[ name ] = offset_skelmeshbuilder;
    }

    for( int child_index = 0; child_index < node->mNumChildren; ++child_index ) {
        recurse( node->mChildren[ child_index ], transformation_so_far, name2transformation, name2offset, name2offset_skelmeshbuilder );
    }
}

void save_rig( const aiScene* scene )
{
    printf( "# Saving the rig.\n" );

    assert( scene );
    assert( scene->mRootNode );

    StringToMatrix4x4 node2transformation, node2offset, name2offset_skelmeshbuilder;
    /// Q: WHY WHY WHY WHY WHY
    /// A: Because on MD5Loader.cpp:190 the root node's transformation is set to the
    ///    inverse of this, because:
    ///    "// Now rotate the whole scene 90 degrees around the x axis to match our internal coordinate system"
    const aiMatrix4x4 I( 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1 );
    recurse( scene->mRootNode, I, node2transformation, node2offset, name2offset_skelmeshbuilder );
    std::cout << "## name2transformation\n";
    print( node2transformation );
    std::cout << "## name2offset\n";
    print( node2offset );
    std::cout << "## name2offset the way SkeletonMeshBuilder does it\n";
    print( name2offset_skelmeshbuilder );

    /// 1 Find the immediate parent of each bone

    // TODO: Save the bones. There should be some kind of nodes with their
    //       names and positions. (We might also need the offsetmatrix from the bone itself).
    //       Store a map of name to index.
    //       See (maybe): http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html
    /*
import pyassimp
filename = '/Users/yotam/Work/ext/three.js/examples/models/collada/monster/monster.dae'
scene = pyassimp.load( filename )
spaces = 0
def recurse( node ):
    global spaces
    print (' '*spaces), node.name #, node.transformation
    spaces += 1
    for child in node.children: recurse( child )
    spaces -= 1

recurse( scene.rootnode )
    */

    // Meshes have bones. Iterate over meshes.
    for( int mesh_index = 0; mesh_index < scene->mNumMeshes; ++mesh_index ) {
        printf( "Mesh %d\n", mesh_index );
        save_rig( scene->mMeshes[ mesh_index ] );
    }
}
#endif

void usage( const char* argv0, std::ostream& out )
{
    out << "Usage: " << argv0 << " path/to/input path/to/output" << std::endl;

    out << std::endl;
    print_importers( out );

    out << std::endl;
    print_exporters( out );
}

int main( int argc, char* argv[] )
{
    /// We need three arguments: the program, the input path, and the output path.
    if( 3 != argc ) {
        usage( argv[0], std::cerr );
        return -1;
    }

    /// Store the input and output paths.
    const char* inpath = argv[1];
    const char* outpath = argv[2];

    // Exit if the output path already exists.
    if( os_path_exists( outpath ) ) {
        std::cerr << "ERROR: Output path exists. Not clobbering: " << outpath << std::endl;
        usage( argv[0], std::cerr );
        return -1;
    }

    /// Get the extension of the output path and its corresponding ASSIMP id.
    std::string extension = os_path_splitext( outpath ).second;
    // os_path_splitext.second returns an extension of the form ".obj".
    // We want the substring from position 1 to the end.
    if( extension.size() <= 1 ) {
        std::cerr << "ERROR: No extension detected on the output path: " << extension << std::endl;
        usage( argv[0], std::cerr );
        return -1;
    }
    extension = extension.substr(1);

    const char* exportId = IdFromExtension( extension );
    // Exit if we couldn't find a corresponding ASSIMP id.
    if( nullptr == exportId ) {
        std::cerr << "ERROR: Output extension unsupported: " << extension << std::endl;
        usage( argv[0], std::cerr );
        return -1;
    }

    /// Load the scene.
    const aiScene* scene = aiImportFile( inpath, 0 );
    if( nullptr == scene ) {
        std::cerr << "ERROR: " << aiGetErrorString() << std::endl;
    }
    std::cout << "Loaded: " << inpath << std::endl;

    /// Save the scene.
    const aiReturn result = aiExportScene( scene, exportId, outpath, 0 );
    if( aiReturn_SUCCESS != result ) {
        std::cerr << "ERROR: Could not save the scene: " << ( (aiReturn_OUTOFMEMORY == result) ? "Out of memory" : "Unknown reason" ) << std::endl;
    }
    std::cout << "Saved: " << outpath << std::endl;

#if SAVE_RIG
    /// Save the rig.
    save_rig( scene );
#endif

    // Cleanup.
    aiReleaseImport( scene );

    return 0;
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "gflags/gflags.h"
#include "gtest/gtest.h"
#include "testsupport/fileutils.h"
#include "testsupport/metrics/video_metrics.h"
#include "video_engine/test/auto_test/automated/vie_integration_test_base.h"
#include "video_engine/test/auto_test/helpers/vie_to_file_renderer.h"
#include "video_engine/test/auto_test/interface/vie_autotest.h"
#include "video_engine/test/auto_test/interface/vie_file_based_comparison_tests.h"
#include "video_engine/test/auto_test/primitives/framedrop_primitives.h"

namespace {

// The input file must be QCIF since I420 gets scaled to that in the tests
// (it is so bandwidth-heavy we have no choice). Our comparison algorithms
// wouldn't like scaling, so this will work when we compare with the original.
const int kInputWidth = 176;
const int kInputHeight = 144;

const double kMinPSNR420DefaultBitRateQCIF = 28;
const double kMinSSIM420DefaultBitRateQCIF = 0.95;
const double kMinPSNRCodecTestsQCIF = 20;
const double kMinSSIMCodecTestsQCIF = 0.7;
const double kMinPSNR50kbpsQCIF = 25;
const double kMinSSIM50kbpsQCIF = 0.8;

class ViEVideoVerificationTest : public testing::Test {
 protected:
  void SetUp() {
    input_file_ = webrtc::test::ResourcePath("paris_qcif", "yuv");
    local_file_renderer_ = new ViEToFileRenderer();
    remote_file_renderer_ = new ViEToFileRenderer();
    SetUpLocalFileRenderer(local_file_renderer_);
    SetUpRemoteFileRenderer(remote_file_renderer_);
  }

  void TearDown() {
    TearDownFileRenderer(local_file_renderer_);
    delete local_file_renderer_;
    TearDownFileRenderer(remote_file_renderer_);
    delete remote_file_renderer_;
  }

  void SetUpLocalFileRenderer(ViEToFileRenderer* file_renderer) {
    SetUpFileRenderer(file_renderer, "-local-preview.yuv");
  }

  void SetUpRemoteFileRenderer(ViEToFileRenderer* file_renderer) {
    SetUpFileRenderer(file_renderer, "-remote.yuv");
  }

  // Must be called manually inside the tests.
  void StopRenderers() {
    local_file_renderer_->StopRendering();
    remote_file_renderer_->StopRendering();
  }

  void TearDownFileRenderer(ViEToFileRenderer* file_renderer) {
    bool test_failed = ::testing::UnitTest::GetInstance()->
        current_test_info()->result()->Failed();
    if (test_failed) {
      // Leave the files for analysis if the test failed.
      file_renderer->SaveOutputFile("failed-");
    } else {
      // No reason to keep the files if we succeeded.
      file_renderer->DeleteOutputFile();
    }
  }

  void CompareFiles(const std::string& reference_file,
                    const std::string& test_file,
                    double minimum_psnr, double minimum_ssim) {
    webrtc::test::QualityMetricsResult psnr;
    int error = I420PSNRFromFiles(reference_file.c_str(), test_file.c_str(),
                                  kInputWidth, kInputHeight, &psnr);
    EXPECT_EQ(0, error) << "PSNR routine failed - output files missing?";
    EXPECT_GT(psnr.average, minimum_psnr);

    webrtc::test::QualityMetricsResult ssim;
    error = I420SSIMFromFiles(reference_file.c_str(), test_file.c_str(),
                              kInputWidth, kInputHeight, &ssim);
    EXPECT_EQ(0, error) << "SSIM routine failed - output files missing?";
    EXPECT_GT(ssim.average, minimum_ssim);  // 1 = perfect, -1 = terrible

    ViETest::Log("Results: PSNR: %f (db)   SSIM: %f",
                 psnr.average, ssim.average);
  }

  std::string input_file_;
  ViEToFileRenderer* local_file_renderer_;
  ViEToFileRenderer* remote_file_renderer_;
  ViEFileBasedComparisonTests tests_;

 private:
  void SetUpFileRenderer(ViEToFileRenderer* file_renderer,
                         const std::string& suffix) {
    std::string test_case_name =
        ::testing::UnitTest::GetInstance()->current_test_info()->name();

    std::string output_path = ViETest::GetResultOutputPath();
    std::string filename = test_case_name + suffix;

    if (!file_renderer->PrepareForRendering(output_path, filename)) {
      FAIL() << "Could not open output file " << filename <<
          " for writing.";
    }
  }
};

TEST_F(ViEVideoVerificationTest, RunsBaseStandardTestWithoutErrors)  {
  ASSERT_TRUE(tests_.TestCallSetup(input_file_, kInputWidth, kInputHeight,
                                   local_file_renderer_,
                                   remote_file_renderer_));
  std::string output_file = remote_file_renderer_->GetFullOutputPath();
  StopRenderers();
  CompareFiles(input_file_, output_file, kMinPSNR420DefaultBitRateQCIF,
               kMinSSIM420DefaultBitRateQCIF);
}

TEST_F(ViEVideoVerificationTest, RunsCodecTestWithoutErrors)  {
  ASSERT_TRUE(tests_.TestCodecs(input_file_, kInputWidth, kInputHeight,
                                local_file_renderer_,
                                remote_file_renderer_));
  std::string reference_file = local_file_renderer_->GetFullOutputPath();
  std::string output_file = remote_file_renderer_->GetFullOutputPath();
  StopRenderers();

  // We compare the local and remote here instead of with the original.
  // The reason is that it is hard to say when the three consecutive tests
  // switch over into each other, at which point we would have to restart the
  // original to get a fair comparison.
  CompareFiles(reference_file, output_file, kMinPSNRCodecTestsQCIF,
               kMinSSIMCodecTestsQCIF);

  // TODO(phoglund): The values should be higher. Investigate why the remote
  // file turns out 6 seconds shorter than the local file (frame dropping?).
}

// Runs a whole stack processing with tracking of which frames are dropped
// in the encoder. The local and remote file will not be of equal size because
// of unknown reasons. Tests show that they start at the same frame, which is
// the important thing when doing frame-to-frame comparison with PSNR/SSIM.
TEST_F(ViEVideoVerificationTest, RunsFullStackWithoutErrors)  {
  // Use our own FrameDropMonitoringRemoteFileRenderer instead of the
  // ViEToFileRenderer from the test fixture:
  // TODO(kjellander): Find a better way to reuse this code without duplication.
  remote_file_renderer_->StopRendering();
  TearDownFileRenderer(remote_file_renderer_);
  delete remote_file_renderer_;

  FrameDropDetector detector;
  remote_file_renderer_ = new FrameDropMonitoringRemoteFileRenderer(&detector);
  SetUpRemoteFileRenderer(remote_file_renderer_);

  // Set a low bit rate so the encoder budget will be tight, causing it to drop
  // frames every now and then.
  const int kBitRateKbps = 50;
  ViETest::Log("Bit rate: %d kbps.\n", kBitRateKbps);
  tests_.TestFullStack(input_file_, kInputWidth, kInputHeight, kBitRateKbps,
                       local_file_renderer_, remote_file_renderer_, &detector);
  const std::string reference_file = local_file_renderer_->GetFullOutputPath();
  const std::string output_file = remote_file_renderer_->GetFullOutputPath();
  StopRenderers();

  ASSERT_EQ(detector.GetFramesDroppedAtRenderStep().size(),
      detector.GetFramesDroppedAtDecodeStep().size())
      << "The number of dropped frames on the decode and render are not equal, "
      "this may be because we have a major problem in the jitter buffer?";

  detector.PrintReport();

  // We may have dropped frames during the processing, which means the output
  // file does not contain all the frames that are present in the input file.
  // To make the quality measurement correct, we must adjust the output file to
  // that by copying the last successful frame into the place where the dropped
  // frame would be, for all dropped frames.
  const int frame_length_in_bytes = 3 * kInputHeight * kInputWidth / 2;
  int num_frames = detector.NumberSentFrames();
  ViETest::Log("Frame length: %d bytes\n", frame_length_in_bytes);
  FixOutputFileForComparison(output_file, num_frames, frame_length_in_bytes,
                             detector.GetFramesDroppedAtDecodeStep());

  // Verify all sent frames are present in the output file.
  size_t output_file_size = webrtc::test::GetFileSize(output_file);
  EXPECT_EQ(num_frames,
      static_cast<int>(output_file_size / frame_length_in_bytes))
      << "The output file size is incorrect. It should be equal to the number"
      "of frames multiplied by the frame size. This will likely affect "
      "PSNR/SSIM calculations in a bad way.";
  CompareFiles(reference_file, output_file, kMinPSNR50kbpsQCIF,
               kMinSSIM50kbpsQCIF);
}

}  // namespace
<commit_msg>Prepared tests for running on build bot.<commit_after>/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "gflags/gflags.h"
#include "gtest/gtest.h"
#include "testsupport/fileutils.h"
#include "testsupport/metrics/video_metrics.h"
#include "video_engine/test/auto_test/automated/vie_integration_test_base.h"
#include "video_engine/test/auto_test/helpers/vie_to_file_renderer.h"
#include "video_engine/test/auto_test/interface/vie_autotest.h"
#include "video_engine/test/auto_test/interface/vie_file_based_comparison_tests.h"
#include "video_engine/test/auto_test/primitives/framedrop_primitives.h"

namespace {

// The input file must be QCIF since I420 gets scaled to that in the tests
// (it is so bandwidth-heavy we have no choice). Our comparison algorithms
// wouldn't like scaling, so this will work when we compare with the original.
const int kInputWidth = 176;
const int kInputHeight = 144;

class ViEVideoVerificationTest : public testing::Test {
 protected:
  void SetUp() {
    input_file_ = webrtc::test::ResourcePath("paris_qcif", "yuv");
    local_file_renderer_ = new ViEToFileRenderer();
    remote_file_renderer_ = new ViEToFileRenderer();
    SetUpLocalFileRenderer(local_file_renderer_);
    SetUpRemoteFileRenderer(remote_file_renderer_);
  }

  void TearDown() {
    TearDownFileRenderer(local_file_renderer_);
    delete local_file_renderer_;
    TearDownFileRenderer(remote_file_renderer_);
    delete remote_file_renderer_;
  }

  void SetUpLocalFileRenderer(ViEToFileRenderer* file_renderer) {
    SetUpFileRenderer(file_renderer, "-local-preview.yuv");
  }

  void SetUpRemoteFileRenderer(ViEToFileRenderer* file_renderer) {
    SetUpFileRenderer(file_renderer, "-remote.yuv");
  }

  // Must be called manually inside the tests.
  void StopRenderers() {
    local_file_renderer_->StopRendering();
    remote_file_renderer_->StopRendering();
  }

  void TearDownFileRenderer(ViEToFileRenderer* file_renderer) {
    bool test_failed = ::testing::UnitTest::GetInstance()->
        current_test_info()->result()->Failed();
    if (test_failed) {
      // Leave the files for analysis if the test failed.
      file_renderer->SaveOutputFile("failed-");
    } else {
      // No reason to keep the files if we succeeded.
      file_renderer->DeleteOutputFile();
    }
  }

  void CompareFiles(const std::string& reference_file,
                    const std::string& test_file,
                    double minimum_psnr, double minimum_ssim) {
    static const char* kPsnrSsimExplanation =
        "Don't worry too much about this error if it only happens once. "
        "It may be because mundane things like unfortunate OS scheduling. "
        "If it keeps happening over and over though it's a cause of concern.";

    webrtc::test::QualityMetricsResult psnr;
    int error = I420PSNRFromFiles(reference_file.c_str(), test_file.c_str(),
                                  kInputWidth, kInputHeight, &psnr);

    EXPECT_EQ(0, error) << "PSNR routine failed - output files missing?";
    EXPECT_GT(psnr.average, minimum_psnr) << kPsnrSsimExplanation;

    webrtc::test::QualityMetricsResult ssim;
    error = I420SSIMFromFiles(reference_file.c_str(), test_file.c_str(),
                              kInputWidth, kInputHeight, &ssim);
    EXPECT_EQ(0, error) << "SSIM routine failed - output files missing?";
    EXPECT_GT(ssim.average, minimum_ssim) << kPsnrSsimExplanation;

    ViETest::Log("Results: PSNR is %f (dB), SSIM is %f (1 is perfect)",
                 psnr.average, ssim.average);
  }

  std::string input_file_;
  ViEToFileRenderer* local_file_renderer_;
  ViEToFileRenderer* remote_file_renderer_;
  ViEFileBasedComparisonTests tests_;

 private:
  void SetUpFileRenderer(ViEToFileRenderer* file_renderer,
                         const std::string& suffix) {
    std::string test_case_name =
        ::testing::UnitTest::GetInstance()->current_test_info()->name();

    std::string output_path = ViETest::GetResultOutputPath();
    std::string filename = test_case_name + suffix;

    if (!file_renderer->PrepareForRendering(output_path, filename)) {
      FAIL() << "Could not open output file " << filename <<
          " for writing.";
    }
  }
};

TEST_F(ViEVideoVerificationTest, RunsBaseStandardTestWithoutErrors) {
  ASSERT_TRUE(tests_.TestCallSetup(input_file_, kInputWidth, kInputHeight,
                                   local_file_renderer_,
                                   remote_file_renderer_));
  std::string output_file = remote_file_renderer_->GetFullOutputPath();
  StopRenderers();

  // The I420 test should give pretty good values since it's a lossless codec
  // running on the default bitrate. It should average about 30 dB but there
  // may be cases where it dips as low as 26 under adverse conditions.
  const double kExpectedMinimumPSNR = 28;
  const double kExpectedMinimumSSIM = 0.95;
  CompareFiles(input_file_, output_file, kExpectedMinimumPSNR,
               kExpectedMinimumSSIM);
}

TEST_F(ViEVideoVerificationTest, RunsCodecTestWithoutErrors)  {
  ASSERT_TRUE(tests_.TestCodecs(input_file_, kInputWidth, kInputHeight,
                                local_file_renderer_,
                                remote_file_renderer_));
  std::string reference_file = local_file_renderer_->GetFullOutputPath();
  std::string output_file = remote_file_renderer_->GetFullOutputPath();
  StopRenderers();

  // We compare the local and remote here instead of with the original.
  // The reason is that it is hard to say when the three consecutive tests
  // switch over into each other, at which point we would have to restart the
  // original to get a fair comparison.
  //
  // The PSNR and SSIM values are quite low here, and they have to be since
  // the codec switches will lead to lag in the output. This is considered
  // acceptable, but it probably shouldn't get worse than this.
  const double kExpectedMinimumPSNR = 20;
  const double kExpectedMinimumSSIM = 0.7;
  CompareFiles(reference_file, output_file, kExpectedMinimumPSNR,
               kExpectedMinimumSSIM);
}

// Runs a whole stack processing with tracking of which frames are dropped
// in the encoder. The local and remote file will not be of equal size because
// of unknown reasons. Tests show that they start at the same frame, which is
// the important thing when doing frame-to-frame comparison with PSNR/SSIM.
TEST_F(ViEVideoVerificationTest, RunsFullStackWithoutErrors) {
  // Use our own FrameDropMonitoringRemoteFileRenderer instead of the
  // ViEToFileRenderer from the test fixture:
  // TODO(kjellander): Find a better way to reuse this code without duplication.
  remote_file_renderer_->StopRendering();
  TearDownFileRenderer(remote_file_renderer_);
  delete remote_file_renderer_;

  FrameDropDetector detector;
  remote_file_renderer_ = new FrameDropMonitoringRemoteFileRenderer(&detector);
  SetUpRemoteFileRenderer(remote_file_renderer_);

  // Set a low bit rate so the encoder budget will be tight, causing it to drop
  // frames every now and then.
  const int kBitRateKbps = 50;
  ViETest::Log("Bit rate: %d kbps.\n", kBitRateKbps);
  tests_.TestFullStack(input_file_, kInputWidth, kInputHeight, kBitRateKbps,
                       local_file_renderer_, remote_file_renderer_, &detector);
  const std::string reference_file = local_file_renderer_->GetFullOutputPath();
  const std::string output_file = remote_file_renderer_->GetFullOutputPath();
  StopRenderers();

  ASSERT_EQ(detector.GetFramesDroppedAtRenderStep().size(),
      detector.GetFramesDroppedAtDecodeStep().size())
      << "The number of dropped frames on the decode and render are not equal, "
      "this may be because we have a major problem in the jitter buffer?";

  detector.PrintReport();

  // We may have dropped frames during the processing, which means the output
  // file does not contain all the frames that are present in the input file.
  // To make the quality measurement correct, we must adjust the output file to
  // that by copying the last successful frame into the place where the dropped
  // frame would be, for all dropped frames.
  const int frame_length_in_bytes = 3 * kInputHeight * kInputWidth / 2;
  int num_frames = detector.NumberSentFrames();
  ViETest::Log("Frame length: %d bytes\n", frame_length_in_bytes);
  FixOutputFileForComparison(output_file, num_frames, frame_length_in_bytes,
                             detector.GetFramesDroppedAtDecodeStep());

  // Verify all sent frames are present in the output file.
  size_t output_file_size = webrtc::test::GetFileSize(output_file);
  EXPECT_EQ(num_frames,
      static_cast<int>(output_file_size / frame_length_in_bytes))
      << "The output file size is incorrect. It should be equal to the number"
      "of frames multiplied by the frame size. This will likely affect "
      "PSNR/SSIM calculations in a bad way.";

  // We are running on a lower bitrate here so we need to settle for somewhat
  // lower PSNR and SSIM values.
  const double kExpectedMinimumPSNR = 25;
  const double kExpectedMinimumSSIM = 0.8;
  CompareFiles(reference_file, output_file, kExpectedMinimumPSNR,
               kExpectedMinimumSSIM);
}

}  // namespace
<|endoftext|>


<commit_before>// Copyright (c) 2016 Bitcoin Unlimited Developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

// All global variables that have construction/destruction dependencies
// must be placed in this file so that the ctor/dtor order is correct.

// Independent global variables may be placed here for organizational
// purposes.

#include "addrman.h"
#include "chain.h"
#include "chainparams.h"
#include "clientversion.h"
#include "consensus/consensus.h"
#include "consensus/params.h"
#include "consensus/validation.h"
#include "dosman.h"
#include "leakybucket.h"
#include "main.h"
#include "miner.h"
#include "netbase.h"
#include "nodestate.h"
#include "policy/policy.h"
#include "primitives/block.h"
#include "requestManager.h"
#include "rpc/server.h"
#include "script/standard.h"
#include "stat.h"
#include "thinblock.h"
#include "timedata.h"
#include "tinyformat.h"
#include "tweak.h"
#include "txmempool.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
#include "validationinterface.h"
#include "version.h"

#include <atomic>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <inttypes.h>
#include <iomanip>
#include <list>
#include <queue>

using namespace std;

#ifdef DEBUG_LOCKORDER
boost::mutex dd_mutex;
std::map<std::pair<void *, void *>, LockStack> lockorders;
boost::thread_specific_ptr<LockStack> lockstack;
#endif

std::atomic<bool> fIsInitialBlockDownload{false};
std::atomic<bool> fRescan{false}; // this flag is set to true when a wallet rescan has been invoked.

CStatusString statusStrings;
// main.cpp CriticalSections:
CCriticalSection cs_LastBlockFile;
CCriticalSection cs_nBlockSequenceId;

CCriticalSection cs_nTimeOffset;
int64_t nTimeOffset = 0;

CCriticalSection cs_rpcWarmup;

CCriticalSection cs_main;
BlockMap mapBlockIndex;
CChain chainActive;
CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange;

proxyType proxyInfo[NET_MAX];
proxyType nameProxy;
CCriticalSection cs_proxyInfos;

// moved from main.cpp (now part of nodestate.h)
std::map<uint256, pair<NodeId, std::list<QueuedBlock>::iterator> > mapBlocksInFlight;
std::map<NodeId, CNodeState> mapNodeState;

set<uint256> setPreVerifiedTxHash;
set<uint256> setUnVerifiedOrphanTxHash;
CCriticalSection cs_xval;
CCriticalSection cs_vNodes;
CCriticalSection cs_mapLocalHost;
map<CNetAddr, LocalServiceInfo> mapLocalHost;
uint64_t CNode::nTotalBytesRecv = 0;
uint64_t CNode::nTotalBytesSent = 0;
CCriticalSection CNode::cs_totalBytesRecv;
CCriticalSection CNode::cs_totalBytesSent;

// critical sections from net.cpp
CCriticalSection cs_setservAddNodeAddresses;
CCriticalSection cs_vAddedNodes;
CCriticalSection cs_vUseDNSSeeds;
CCriticalSection cs_mapInboundConnectionTracker;
CCriticalSection cs_vOneShots;

CCriticalSection cs_statMap;

deque<string> vOneShots;
std::map<CNetAddr, ConnectionHistory> mapInboundConnectionTracker;
vector<std::string> vUseDNSSeeds;
vector<std::string> vAddedNodes;
set<CNetAddr> setservAddNodeAddresses;

uint64_t maxGeneratedBlock = DEFAULT_BLOCK_MAX_SIZE;
uint64_t excessiveBlockSize = DEFAULT_EXCESSIVE_BLOCK_SIZE;
unsigned int excessiveAcceptDepth = DEFAULT_EXCESSIVE_ACCEPT_DEPTH;
unsigned int maxMessageSizeMultiplier = DEFAULT_MAX_MESSAGE_SIZE_MULTIPLIER;
int nMaxOutConnections = DEFAULT_MAX_OUTBOUND_CONNECTIONS;

uint32_t blockVersion = 0; // Overrides the mined block version if non-zero

std::vector<std::string> BUComments = std::vector<std::string>();
std::string minerComment;

CLeakyBucket receiveShaper(DEFAULT_MAX_RECV_BURST, DEFAULT_AVE_RECV);
CLeakyBucket sendShaper(DEFAULT_MAX_SEND_BURST, DEFAULT_AVE_SEND);
boost::chrono::steady_clock CLeakyBucket::clock;

// Variables for statistics tracking, must be before the "requester" singleton instantiation
const char *sampleNames[] = {"sec10", "min5", "hourly", "daily", "monthly"};
int operateSampleCount[] = {30, 12, 24, 30};
int interruptIntervals[] = {30, 30 * 12, 30 * 12 * 24, 30 * 12 * 24 * 30};

CTxMemPool mempool(::minRelayTxFee);

std::chrono::milliseconds statMinInterval(10000);
boost::asio::io_service stat_io_service;

std::list<CStatBase *> mallocedStats;
CStatMap statistics;
CTweakMap tweaks;

map<CInv, CDataStream> mapRelay;
deque<pair<int64_t, CInv> > vRelayExpiration;
CCriticalSection cs_mapRelay;
limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);

vector<CNode *> vNodes;
list<CNode *> vNodesDisconnected;
CSemaphore *semOutbound = NULL;
CSemaphore *semOutboundAddNode = NULL; // BU: separate semaphore for -addnodes
CNodeSignals g_signals;
CAddrMan addrman;
CDoSManager dosMan;

// BU: change locking of orphan map from using cs_main to cs_orphancache.  There is too much dependance on cs_main locks
// which are generally too broad in scope.
CCriticalSection cs_orphancache;
map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_orphancache);
map<uint256, set<uint256> > mapOrphanTransactionsByPrev GUARDED_BY(cs_orphancache);

CTweakRef<uint64_t> ebTweak("net.excessiveBlock",
    "Excessive block size in bytes",
    &excessiveBlockSize,
    &ExcessiveBlockValidator);
CTweak<uint64_t> blockSigopsPerMb("net.excessiveSigopsPerMb",
    "Excessive effort per block, denoted in cost (# inputs * txsize) per MB",
    BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS);
CTweak<uint64_t> blockMiningSigopsPerMb("mining.excessiveSigopsPerMb",
    "Excessive effort per block, denoted in cost (# inputs * txsize) per MB",
    BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS);
CTweak<uint64_t> coinbaseReserve("mining.coinbaseReserve",
    "How much space to reserve for the coinbase transaction, in bytes",
    DEFAULT_COINBASE_RESERVE_SIZE);
CTweakRef<std::string> miningCommentTweak("mining.comment", "Include text in a block's coinbase.", &minerComment);
CTweakRef<uint64_t> miningBlockSize("mining.blockSize",
    "Maximum block size in bytes.  The maximum block size returned from 'getblocktemplate' will be this value minus "
    "mining.coinbaseReserve.",
    &maxGeneratedBlock,
    &MiningBlockSizeValidator);
CTweakRef<unsigned int> maxDataCarrierTweak("mining.dataCarrierSize",
    "Maximum size of OP_RETURN data script in bytes.",
    &nMaxDatacarrierBytes);

CTweak<uint64_t> miningForkTime("mining.forkMay2018Time",
    "Time in seconds since the epoch to initiate a hard fork scheduled on 15th May 2018.",
    1526400000); // Tue 15 May 2018 18:00:00 UTC

CTweak<bool> unsafeGetBlockTemplate("mining.unsafeGetBlockTemplate",
    "Allow getblocktemplate to succeed even if the chain tip is old or this node is not connected to other nodes",
    false);

CTweak<uint64_t> miningForkEB("mining.forkExcessiveBlock",
    "Set the excessive block to this value at the time of the fork.",
    32000000); // May2018 HF proposed max block size
CTweak<uint64_t> miningForkMG("mining.forkBlockSize",
    "Set the maximum block generation size to this value at the time of the fork.",
    8000000);

CTweak<bool> walletSignWithForkSig("wallet.useNewSig",
    "Once the fork occurs, sign transactions using the new signature scheme so that they will only be valid on the "
    "fork.",
    true);

CTweak<unsigned int> maxTxSize("net.excessiveTx", "Largest transaction size in bytes", DEFAULT_LARGEST_TRANSACTION);
CTweakRef<unsigned int> eadTweak("net.excessiveAcceptDepth",
    "Excessive block chain acceptance depth in blocks",
    &excessiveAcceptDepth,
    &AcceptDepthValidator);
CTweakRef<int> maxOutConnectionsTweak("net.maxOutboundConnections",
    "Maximum number of outbound connections",
    &nMaxOutConnections,
    &OutboundConnectionValidator);
CTweakRef<int> maxConnectionsTweak("net.maxConnections", "Maximum number of connections", &nMaxConnections);
CTweakRef<int> minXthinNodesTweak("net.minXthinNodes",
    "Minimum number of outbound xthin capable nodes to connect to",
    &nMinXthinNodes);
// When should I request a tx from someone else (in microseconds). cmdline/bitcoin.conf: -txretryinterval
CTweakRef<unsigned int> triTweak("net.txRetryInterval",
    "How long to wait in microseconds before requesting a transaction from another source",
    &MIN_TX_REQUEST_RETRY_INTERVAL);
// When should I request a block from someone else (in microseconds). cmdline/bitcoin.conf: -blkretryinterval
CTweakRef<unsigned int> briTweak("net.blockRetryInterval",
    "How long to wait in microseconds before requesting a block from another source",
    &MIN_BLK_REQUEST_RETRY_INTERVAL);

CTweakRef<std::string> subverOverrideTweak("net.subversionOverride",
    "If set, this field will override the normal subversion field.  This is useful if you need to hide your node.",
    &subverOverride,
    &SubverValidator);

CTweak<CAmount> maxTxFee("wallet.maxTxFee",
    "Maximum total fees to use in a single wallet transaction or raw transaction; setting this too low may abort large "
    "transactions.",
    DEFAULT_TRANSACTION_MAXFEE);

/** Number of blocks that can be requested at any given time from a single peer. */
CTweak<unsigned int> maxBlocksInTransitPerPeer("net.maxBlocksInTransitPerPeer",
    "Number of blocks that can be requested at any given time from a single peer. 0 means use algorithm.",
    0);
/** Size of the "block download window": how far ahead of our current height do we fetch?
 *  Larger windows tolerate larger download speed differences between peer, but increase the potential
 *  degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning
 *  harder). We'll probably want to make this a per-peer adaptive value at some point. */
CTweak<unsigned int> blockDownloadWindow("net.blockDownloadWindow",
    "How far ahead of our current height do we fetch? 0 means use algorithm.",
    0);

/** This is the initial size of CFileBuffer's RAM buffer during reindex.  A
larger size will result in a tiny bit better performance if blocks are that
size.
The real purpose of this parameter is to exhaustively test dynamic buffer resizes
during reindexing by allowing the size to be set to low and random values.
*/
CTweak<uint64_t> reindexTypicalBlockSize("reindex.typicalBlockSize",
    "Set larger than the typical block size.  The block data file's RAM buffer will initally be 2x this size.",
    TYPICAL_BLOCK_SIZE);

/** This is the initial size of CFileBuffer's RAM buffer during reindex.  A
larger size will result in a tiny bit better performance if blocks are that
size.
The real purpose of this parameter is to exhaustively test dynamic buffer resizes
during reindexing by allowing the size to be set to low and random values.
*/
CTweak<uint64_t> checkScriptDays("blockchain.checkScriptDays",
    "The number of days in the past we check scripts during initial block download.",
    DEFAULT_CHECKPOINT_DAYS);

CRequestManager requester; // after the maps nodes and tweaks

CStatHistory<unsigned int> txAdded; //"memPool/txAdded");
CStatHistory<uint64_t, MinValMax<uint64_t> > poolSize; // "memPool/size",STAT_OP_AVE);
CStatHistory<uint64_t> recvAmt;
CStatHistory<uint64_t> sendAmt;
CStatHistory<uint64_t> nTxValidationTime("txValidationTime", STAT_OP_MAX | STAT_INDIVIDUAL);
CCriticalSection cs_blockvalidationtime;
CStatHistory<uint64_t> nBlockValidationTime("blockValidationTime", STAT_OP_MAX | STAT_INDIVIDUAL);

CThinBlockData thindata; // Singleton class

uint256 bitcoinCashForkBlockHash = uint256S("000000000000000000651ef99cb9fcbe0dadde1d424bd9f15ff20136191a5eec");

#ifdef ENABLE_MUTRACE
class CPrintSomePointers
{
public:
    CPrintSomePointers()
    {
        printf("csBestBlock %p\n", &csBestBlock);
        printf("cvBlockChange %p\n", &cvBlockChange);
        printf("cs_LastBlockFile %p\n", &cs_LastBlockFile);
        printf("cs_nBlockSequenceId %p\n", &cs_nBlockSequenceId);
        printf("cs_nTimeOffset %p\n", &cs_nTimeOffset);
        printf("cs_rpcWarmup %p\n", &cs_rpcWarmup);
        printf("cs_main %p\n", &cs_main);
        printf("csBestBlock %p\n", &csBestBlock);
        printf("cs_proxyInfos %p\n", &cs_proxyInfos);
        printf("cs_xval %p\n", &cs_xval);
        printf("cs_vNodes %p\n", &cs_vNodes);
        printf("cs_mapLocalHost %p\n", &cs_mapLocalHost);
        printf("CNode::cs_totalBytesRecv %p\n", &CNode::cs_totalBytesRecv);
        printf("CNode::cs_totalBytesSent %p\n", &CNode::cs_totalBytesSent);

        // critical sections from net.cpp
        printf("cs_setservAddNodeAddresses %p\n", &cs_setservAddNodeAddresses);
        printf("cs_vAddedNodes %p\n", &cs_vAddedNodes);
        printf("cs_vUseDNSSeeds %p\n", &cs_vUseDNSSeeds);
        printf("cs_mapInboundConnectionTracker %p\n", &cs_mapInboundConnectionTracker);
        printf("cs_vOneShots %p\n", &cs_vOneShots);

        printf("cs_statMap %p\n", &cs_statMap);

        printf("requester.cs_objDownloader %p\n", &requester.cs_objDownloader);

        printf("\nCondition variables:\n");
        printf("cvBlockChange %p\n", &cvBlockChange);
    }
};

static CPrintSomePointers unused;
#endif
<commit_msg>Ccorrect human-readable fork time in comment<commit_after>// Copyright (c) 2016 Bitcoin Unlimited Developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

// All global variables that have construction/destruction dependencies
// must be placed in this file so that the ctor/dtor order is correct.

// Independent global variables may be placed here for organizational
// purposes.

#include "addrman.h"
#include "chain.h"
#include "chainparams.h"
#include "clientversion.h"
#include "consensus/consensus.h"
#include "consensus/params.h"
#include "consensus/validation.h"
#include "dosman.h"
#include "leakybucket.h"
#include "main.h"
#include "miner.h"
#include "netbase.h"
#include "nodestate.h"
#include "policy/policy.h"
#include "primitives/block.h"
#include "requestManager.h"
#include "rpc/server.h"
#include "script/standard.h"
#include "stat.h"
#include "thinblock.h"
#include "timedata.h"
#include "tinyformat.h"
#include "tweak.h"
#include "txmempool.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
#include "validationinterface.h"
#include "version.h"

#include <atomic>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <inttypes.h>
#include <iomanip>
#include <list>
#include <queue>

using namespace std;

#ifdef DEBUG_LOCKORDER
boost::mutex dd_mutex;
std::map<std::pair<void *, void *>, LockStack> lockorders;
boost::thread_specific_ptr<LockStack> lockstack;
#endif

std::atomic<bool> fIsInitialBlockDownload{false};
std::atomic<bool> fRescan{false}; // this flag is set to true when a wallet rescan has been invoked.

CStatusString statusStrings;
// main.cpp CriticalSections:
CCriticalSection cs_LastBlockFile;
CCriticalSection cs_nBlockSequenceId;

CCriticalSection cs_nTimeOffset;
int64_t nTimeOffset = 0;

CCriticalSection cs_rpcWarmup;

CCriticalSection cs_main;
BlockMap mapBlockIndex;
CChain chainActive;
CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange;

proxyType proxyInfo[NET_MAX];
proxyType nameProxy;
CCriticalSection cs_proxyInfos;

// moved from main.cpp (now part of nodestate.h)
std::map<uint256, pair<NodeId, std::list<QueuedBlock>::iterator> > mapBlocksInFlight;
std::map<NodeId, CNodeState> mapNodeState;

set<uint256> setPreVerifiedTxHash;
set<uint256> setUnVerifiedOrphanTxHash;
CCriticalSection cs_xval;
CCriticalSection cs_vNodes;
CCriticalSection cs_mapLocalHost;
map<CNetAddr, LocalServiceInfo> mapLocalHost;
uint64_t CNode::nTotalBytesRecv = 0;
uint64_t CNode::nTotalBytesSent = 0;
CCriticalSection CNode::cs_totalBytesRecv;
CCriticalSection CNode::cs_totalBytesSent;

// critical sections from net.cpp
CCriticalSection cs_setservAddNodeAddresses;
CCriticalSection cs_vAddedNodes;
CCriticalSection cs_vUseDNSSeeds;
CCriticalSection cs_mapInboundConnectionTracker;
CCriticalSection cs_vOneShots;

CCriticalSection cs_statMap;

deque<string> vOneShots;
std::map<CNetAddr, ConnectionHistory> mapInboundConnectionTracker;
vector<std::string> vUseDNSSeeds;
vector<std::string> vAddedNodes;
set<CNetAddr> setservAddNodeAddresses;

uint64_t maxGeneratedBlock = DEFAULT_BLOCK_MAX_SIZE;
uint64_t excessiveBlockSize = DEFAULT_EXCESSIVE_BLOCK_SIZE;
unsigned int excessiveAcceptDepth = DEFAULT_EXCESSIVE_ACCEPT_DEPTH;
unsigned int maxMessageSizeMultiplier = DEFAULT_MAX_MESSAGE_SIZE_MULTIPLIER;
int nMaxOutConnections = DEFAULT_MAX_OUTBOUND_CONNECTIONS;

uint32_t blockVersion = 0; // Overrides the mined block version if non-zero

std::vector<std::string> BUComments = std::vector<std::string>();
std::string minerComment;

CLeakyBucket receiveShaper(DEFAULT_MAX_RECV_BURST, DEFAULT_AVE_RECV);
CLeakyBucket sendShaper(DEFAULT_MAX_SEND_BURST, DEFAULT_AVE_SEND);
boost::chrono::steady_clock CLeakyBucket::clock;

// Variables for statistics tracking, must be before the "requester" singleton instantiation
const char *sampleNames[] = {"sec10", "min5", "hourly", "daily", "monthly"};
int operateSampleCount[] = {30, 12, 24, 30};
int interruptIntervals[] = {30, 30 * 12, 30 * 12 * 24, 30 * 12 * 24 * 30};

CTxMemPool mempool(::minRelayTxFee);

std::chrono::milliseconds statMinInterval(10000);
boost::asio::io_service stat_io_service;

std::list<CStatBase *> mallocedStats;
CStatMap statistics;
CTweakMap tweaks;

map<CInv, CDataStream> mapRelay;
deque<pair<int64_t, CInv> > vRelayExpiration;
CCriticalSection cs_mapRelay;
limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);

vector<CNode *> vNodes;
list<CNode *> vNodesDisconnected;
CSemaphore *semOutbound = NULL;
CSemaphore *semOutboundAddNode = NULL; // BU: separate semaphore for -addnodes
CNodeSignals g_signals;
CAddrMan addrman;
CDoSManager dosMan;

// BU: change locking of orphan map from using cs_main to cs_orphancache.  There is too much dependance on cs_main locks
// which are generally too broad in scope.
CCriticalSection cs_orphancache;
map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_orphancache);
map<uint256, set<uint256> > mapOrphanTransactionsByPrev GUARDED_BY(cs_orphancache);

CTweakRef<uint64_t> ebTweak("net.excessiveBlock",
    "Excessive block size in bytes",
    &excessiveBlockSize,
    &ExcessiveBlockValidator);
CTweak<uint64_t> blockSigopsPerMb("net.excessiveSigopsPerMb",
    "Excessive effort per block, denoted in cost (# inputs * txsize) per MB",
    BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS);
CTweak<uint64_t> blockMiningSigopsPerMb("mining.excessiveSigopsPerMb",
    "Excessive effort per block, denoted in cost (# inputs * txsize) per MB",
    BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS);
CTweak<uint64_t> coinbaseReserve("mining.coinbaseReserve",
    "How much space to reserve for the coinbase transaction, in bytes",
    DEFAULT_COINBASE_RESERVE_SIZE);
CTweakRef<std::string> miningCommentTweak("mining.comment", "Include text in a block's coinbase.", &minerComment);
CTweakRef<uint64_t> miningBlockSize("mining.blockSize",
    "Maximum block size in bytes.  The maximum block size returned from 'getblocktemplate' will be this value minus "
    "mining.coinbaseReserve.",
    &maxGeneratedBlock,
    &MiningBlockSizeValidator);
CTweakRef<unsigned int> maxDataCarrierTweak("mining.dataCarrierSize",
    "Maximum size of OP_RETURN data script in bytes.",
    &nMaxDatacarrierBytes);

CTweak<uint64_t> miningForkTime("mining.forkMay2018Time",
    "Time in seconds since the epoch to initiate a hard fork scheduled on 15th May 2018.",
    1526400000); // Tue 15 May 2018 16:00:00 UTC

CTweak<bool> unsafeGetBlockTemplate("mining.unsafeGetBlockTemplate",
    "Allow getblocktemplate to succeed even if the chain tip is old or this node is not connected to other nodes",
    false);

CTweak<uint64_t> miningForkEB("mining.forkExcessiveBlock",
    "Set the excessive block to this value at the time of the fork.",
    32000000); // May2018 HF proposed max block size
CTweak<uint64_t> miningForkMG("mining.forkBlockSize",
    "Set the maximum block generation size to this value at the time of the fork.",
    8000000);

CTweak<bool> walletSignWithForkSig("wallet.useNewSig",
    "Once the fork occurs, sign transactions using the new signature scheme so that they will only be valid on the "
    "fork.",
    true);

CTweak<unsigned int> maxTxSize("net.excessiveTx", "Largest transaction size in bytes", DEFAULT_LARGEST_TRANSACTION);
CTweakRef<unsigned int> eadTweak("net.excessiveAcceptDepth",
    "Excessive block chain acceptance depth in blocks",
    &excessiveAcceptDepth,
    &AcceptDepthValidator);
CTweakRef<int> maxOutConnectionsTweak("net.maxOutboundConnections",
    "Maximum number of outbound connections",
    &nMaxOutConnections,
    &OutboundConnectionValidator);
CTweakRef<int> maxConnectionsTweak("net.maxConnections", "Maximum number of connections", &nMaxConnections);
CTweakRef<int> minXthinNodesTweak("net.minXthinNodes",
    "Minimum number of outbound xthin capable nodes to connect to",
    &nMinXthinNodes);
// When should I request a tx from someone else (in microseconds). cmdline/bitcoin.conf: -txretryinterval
CTweakRef<unsigned int> triTweak("net.txRetryInterval",
    "How long to wait in microseconds before requesting a transaction from another source",
    &MIN_TX_REQUEST_RETRY_INTERVAL);
// When should I request a block from someone else (in microseconds). cmdline/bitcoin.conf: -blkretryinterval
CTweakRef<unsigned int> briTweak("net.blockRetryInterval",
    "How long to wait in microseconds before requesting a block from another source",
    &MIN_BLK_REQUEST_RETRY_INTERVAL);

CTweakRef<std::string> subverOverrideTweak("net.subversionOverride",
    "If set, this field will override the normal subversion field.  This is useful if you need to hide your node.",
    &subverOverride,
    &SubverValidator);

CTweak<CAmount> maxTxFee("wallet.maxTxFee",
    "Maximum total fees to use in a single wallet transaction or raw transaction; setting this too low may abort large "
    "transactions.",
    DEFAULT_TRANSACTION_MAXFEE);

/** Number of blocks that can be requested at any given time from a single peer. */
CTweak<unsigned int> maxBlocksInTransitPerPeer("net.maxBlocksInTransitPerPeer",
    "Number of blocks that can be requested at any given time from a single peer. 0 means use algorithm.",
    0);
/** Size of the "block download window": how far ahead of our current height do we fetch?
 *  Larger windows tolerate larger download speed differences between peer, but increase the potential
 *  degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning
 *  harder). We'll probably want to make this a per-peer adaptive value at some point. */
CTweak<unsigned int> blockDownloadWindow("net.blockDownloadWindow",
    "How far ahead of our current height do we fetch? 0 means use algorithm.",
    0);

/** This is the initial size of CFileBuffer's RAM buffer during reindex.  A
larger size will result in a tiny bit better performance if blocks are that
size.
The real purpose of this parameter is to exhaustively test dynamic buffer resizes
during reindexing by allowing the size to be set to low and random values.
*/
CTweak<uint64_t> reindexTypicalBlockSize("reindex.typicalBlockSize",
    "Set larger than the typical block size.  The block data file's RAM buffer will initally be 2x this size.",
    TYPICAL_BLOCK_SIZE);

/** This is the initial size of CFileBuffer's RAM buffer during reindex.  A
larger size will result in a tiny bit better performance if blocks are that
size.
The real purpose of this parameter is to exhaustively test dynamic buffer resizes
during reindexing by allowing the size to be set to low and random values.
*/
CTweak<uint64_t> checkScriptDays("blockchain.checkScriptDays",
    "The number of days in the past we check scripts during initial block download.",
    DEFAULT_CHECKPOINT_DAYS);

CRequestManager requester; // after the maps nodes and tweaks

CStatHistory<unsigned int> txAdded; //"memPool/txAdded");
CStatHistory<uint64_t, MinValMax<uint64_t> > poolSize; // "memPool/size",STAT_OP_AVE);
CStatHistory<uint64_t> recvAmt;
CStatHistory<uint64_t> sendAmt;
CStatHistory<uint64_t> nTxValidationTime("txValidationTime", STAT_OP_MAX | STAT_INDIVIDUAL);
CCriticalSection cs_blockvalidationtime;
CStatHistory<uint64_t> nBlockValidationTime("blockValidationTime", STAT_OP_MAX | STAT_INDIVIDUAL);

CThinBlockData thindata; // Singleton class

uint256 bitcoinCashForkBlockHash = uint256S("000000000000000000651ef99cb9fcbe0dadde1d424bd9f15ff20136191a5eec");

#ifdef ENABLE_MUTRACE
class CPrintSomePointers
{
public:
    CPrintSomePointers()
    {
        printf("csBestBlock %p\n", &csBestBlock);
        printf("cvBlockChange %p\n", &cvBlockChange);
        printf("cs_LastBlockFile %p\n", &cs_LastBlockFile);
        printf("cs_nBlockSequenceId %p\n", &cs_nBlockSequenceId);
        printf("cs_nTimeOffset %p\n", &cs_nTimeOffset);
        printf("cs_rpcWarmup %p\n", &cs_rpcWarmup);
        printf("cs_main %p\n", &cs_main);
        printf("csBestBlock %p\n", &csBestBlock);
        printf("cs_proxyInfos %p\n", &cs_proxyInfos);
        printf("cs_xval %p\n", &cs_xval);
        printf("cs_vNodes %p\n", &cs_vNodes);
        printf("cs_mapLocalHost %p\n", &cs_mapLocalHost);
        printf("CNode::cs_totalBytesRecv %p\n", &CNode::cs_totalBytesRecv);
        printf("CNode::cs_totalBytesSent %p\n", &CNode::cs_totalBytesSent);

        // critical sections from net.cpp
        printf("cs_setservAddNodeAddresses %p\n", &cs_setservAddNodeAddresses);
        printf("cs_vAddedNodes %p\n", &cs_vAddedNodes);
        printf("cs_vUseDNSSeeds %p\n", &cs_vUseDNSSeeds);
        printf("cs_mapInboundConnectionTracker %p\n", &cs_mapInboundConnectionTracker);
        printf("cs_vOneShots %p\n", &cs_vOneShots);

        printf("cs_statMap %p\n", &cs_statMap);

        printf("requester.cs_objDownloader %p\n", &requester.cs_objDownloader);

        printf("\nCondition variables:\n");
        printf("cvBlockChange %p\n", &cvBlockChange);
    }
};

static CPrintSomePointers unused;
#endif
<|endoftext|>


<commit_before>/** \file
 * \brief Implementation of hexdump()
 *
 * This file contains an implementation of hexdump() which can be used
 * to write a hex dump of arbitrary memory locations to ostreams and
 * log4cpp targets.
 *
 * \author Peter Hille (png!das-system) <peter@das-system-networks.de>
 *
 * \version 1.1 Moved all hexdump related code to hexdump.cc
 *
 * \version 1.0 Initial release
 */
#include <cctype>
#include <cstdio>
#include <cstring>
#include <iostream>

#ifdef WITH_LOG4CPP
#include <log4cpp/CategoryStream.hh>
#endif
#include <dsnutil/hexdump.h>

/** \brief Print hex dump of a memory area
 *
 * This function pretty-prints a hex-dump of the memory area pointed to by
 * \a mem to an ostream. The output is roughly similar to 'hexdump -C'
 * on UNIX systems.
 *
 * \param mem Pointer to the memory area to be dumped
 * \param length Number of bytes to dump
 * \param out Output stream to which the hexdump shall be written
 */
void hexdump(const void* mem, size_t length, std::ostream& out)
{
    char line[80];
    const char* src = static_cast<const char*>(mem);

    for (unsigned int i = 0; i < length; i += 16, src += 16) {
        char* t = line;
        // each line begins with the offset...
        t += sprintf(t, "%04x: ", i);

        // ... then we print the data in hex byte-wise...
        for (int j = 0; j < 16; j++) {
            if (i + j < length)
                t += sprintf(t, "%02X", src[j] & 0xff);
            else
                t += sprintf(t, "  ");

            // print a tabulator after the first group of 8 bytes
            if (j == 7)
                t += sprintf(t, "\t");
            else
                t += sprintf(t, " ");
        }

        // ... and finally we display all printable characters
        t += sprintf(t, "|");
        for (int j = 0; j < 16; j++) {
            if (i + j < length) {
                if (isprint((unsigned char)src[j]))
                    t += sprintf(t, "%c", src[j]);
                else
                    t += sprintf(t, ".");
            } else {
                t += sprintf(t, " ");
            }
        }

        t += sprintf(t, "|\n");
        out << line;
    }
}

#ifdef WITH_LOG4CPP
/** \brief Print hex dump of a memory area
 *
 * This function pretty-prints a hex-dump of the memory area pointed to by
 * \a mem to a log4cpp::CategoryStream.
 * The output is roughly similar to 'hexdump -C' on UNIX systems.
 *
 * \param mem Pointer to the memory area to be dumped
 * \param length Number of bytes to dump
 * \param out Output stream to which the hexdump shall be written
 */
void hexdump(const void* mem, size_t length, log4cpp::CategoryStream out)
{
    char line[80];
    const char* src = static_cast<const char*>(mem);

    out << "Hexdumping " << length << " bytes:\n";

    for (unsigned int i = 0; i < length; i += 16, src += 16) {
        char* t = line;
        // each line begins with the offset...
        t += sprintf(t, "%04x: ", i);

        // ... then we print the data in hex byte-wise...
        for (int j = 0; j < 16; j++) {
            if (i + j < length)
                t += sprintf(t, "%02X", src[j] & 0xff);
            else
                t += sprintf(t, "  ");

            // print a tabulator after the first group of 8 bytes
            if (j == 7)
                t += sprintf(t, "\t");
            else
                t += sprintf(t, " ");
        }

        // ... and finally we display all printable characters
        t += sprintf(t, "|");
        for (int j = 0; j < 16; j++) {
            if (i + j < length) {
                if (isprint((unsigned char)src[j]))
                    t += sprintf(t, "%c", src[j]);
                else
                    t += sprintf(t, ".");
            } else {
                t += sprintf(t, " ");
            }
        }

        t += sprintf(t, "|");
        out << line << "\n";
    }
}
#endif // WITH_LOG4CP
<commit_msg>Disable CRT sprintf() warnings in hexdump on Windows builds.<commit_after>/** \file
 * \brief Implementation of hexdump()
 *
 * This file contains an implementation of hexdump() which can be used
 * to write a hex dump of arbitrary memory locations to ostreams and
 * log4cpp targets.
 *
 * \author Peter Hille (png!das-system) <peter@das-system-networks.de>
 *
 * \version 1.1 Moved all hexdump related code to hexdump.cc
 *
 * \version 1.0 Initial release
 */
#ifdef WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <dsnutil/hexdump.h>

#include <cctype>
#include <cstdio>
#include <cstring>
#include <iostream>

#ifdef WITH_LOG4CPP
#include <log4cpp/CategoryStream.hh>
#endif

/** \brief Print hex dump of a memory area
 *
 * This function pretty-prints a hex-dump of the memory area pointed to by
 * \a mem to an ostream. The output is roughly similar to 'hexdump -C'
 * on UNIX systems.
 *
 * \param mem Pointer to the memory area to be dumped
 * \param length Number of bytes to dump
 * \param out Output stream to which the hexdump shall be written
 */
void hexdump(const void* mem, size_t length, std::ostream& out)
{
    char line[80];
    const char* src = static_cast<const char*>(mem);

    for (unsigned int i = 0; i < length; i += 16, src += 16) {
        char* t = line;
        // each line begins with the offset...
        t += sprintf(t, "%04x: ", i);

        // ... then we print the data in hex byte-wise...
        for (int j = 0; j < 16; j++) {
            if (i + j < length)
                t += sprintf(t, "%02X", src[j] & 0xff);
            else
                t += sprintf(t, "  ");

            // print a tabulator after the first group of 8 bytes
            if (j == 7)
                t += sprintf(t, "\t");
            else
                t += sprintf(t, " ");
        }

        // ... and finally we display all printable characters
        t += sprintf(t, "|");
        for (int j = 0; j < 16; j++) {
            if (i + j < length) {
                if (isprint((unsigned char)src[j]))
                    t += sprintf(t, "%c", src[j]);
                else
                    t += sprintf(t, ".");
            } else {
                t += sprintf(t, " ");
            }
        }

        t += sprintf(t, "|\n");
        out << line;
    }
}

#ifdef WITH_LOG4CPP
/** \brief Print hex dump of a memory area
 *
 * This function pretty-prints a hex-dump of the memory area pointed to by
 * \a mem to a log4cpp::CategoryStream.
 * The output is roughly similar to 'hexdump -C' on UNIX systems.
 *
 * \param mem Pointer to the memory area to be dumped
 * \param length Number of bytes to dump
 * \param out Output stream to which the hexdump shall be written
 */
void hexdump(const void* mem, size_t length, log4cpp::CategoryStream out)
{
    char line[80];
    const char* src = static_cast<const char*>(mem);

    out << "Hexdumping " << length << " bytes:\n";

    for (unsigned int i = 0; i < length; i += 16, src += 16) {
        char* t = line;
        // each line begins with the offset...
        t += sprintf(t, "%04x: ", i);

        // ... then we print the data in hex byte-wise...
        for (int j = 0; j < 16; j++) {
            if (i + j < length)
                t += sprintf(t, "%02X", src[j] & 0xff);
            else
                t += sprintf(t, "  ");

            // print a tabulator after the first group of 8 bytes
            if (j == 7)
                t += sprintf(t, "\t");
            else
                t += sprintf(t, " ");
        }

        // ... and finally we display all printable characters
        t += sprintf(t, "|");
        for (int j = 0; j < 16; j++) {
            if (i + j < length) {
                if (isprint((unsigned char)src[j]))
                    t += sprintf(t, "%c", src[j]);
                else
                    t += sprintf(t, ".");
            } else {
                t += sprintf(t, " ");
            }
        }

        t += sprintf(t, "|");
        out << line << "\n";
    }
}
#endif // WITH_LOG4CP
<|endoftext|>