File: format.cpp

package info (click to toggle)
cataclysm-dda 0.H-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 710,808 kB
  • sloc: cpp: 524,019; python: 11,580; sh: 1,228; makefile: 1,169; xml: 507; javascript: 150; sql: 56; exp: 41; perl: 37
file content (131 lines) | stat: -rw-r--r-- 5,131 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "format.h"
#include "json.h"

#include <functional>
#include <sstream>
#include <string>

static void write_array( TextJsonIn &jsin, JsonOut &jsout, int depth, bool force_wrap )
{
    jsout.start_array( force_wrap );
    jsin.start_array();
    while( !jsin.end_array() ) {
        formatter::format( jsin, jsout, depth );
    }
    jsout.end_array();
}

static void write_object( TextJsonIn &jsin, JsonOut &jsout, int depth, bool force_wrap )
{
    jsout.start_object( force_wrap );
    jsin.start_object();
    while( !jsin.end_object() ) {
        std::string name = jsin.get_member_name();
        jsout.member( name );
        bool override_wrap = false;
        if( name == "rows" || name == "blueprint" || name == "picture" ) {
            // Introspect into the row, if it has more than one element, force it to wrap.
            int in_start_pos = jsin.tell();
            bool ate_separator = jsin.get_ate_separator();
            {
                TextJsonArray arr = jsin.get_array();
                if( arr.size() > 1 ) {
                    override_wrap = true;
                }
            }
            jsin.seek( in_start_pos );
            jsin.set_ate_separator( ate_separator );
        }
        formatter::format( jsin, jsout, depth, override_wrap );
    }
    jsout.end_object();
}

static void format_collection( TextJsonIn &jsin, JsonOut &jsout, int depth,
                               const std::function<void( TextJsonIn &, JsonOut &, int, bool )> &write_func,
                               bool force_wrap )
{
    if( depth > 1 && !force_wrap ) {
        // We're backtracking by storing jsin and jsout state before formatting
        // and restoring it afterwards if necessary.
        int in_start_pos = jsin.tell();
        bool ate_separator = jsin.get_ate_separator();
        int out_start_pos = jsout.tell();
        bool need_separator = jsout.get_need_separator();
        write_func( jsin, jsout, depth, false );
        if( jsout.tell() - out_start_pos <= 120 ) {
            // Line is short enough, so we're done.
            return;
        } else {
            // Reset jsin and jsout to their initial state,
            // and we'll serialize while forcing wrapping.
            jsin.seek( in_start_pos );
            jsin.set_ate_separator( ate_separator );
            jsout.seek( out_start_pos );
            if( need_separator ) {
                jsout.set_need_separator();
            }
        }
    }
    write_func( jsin, jsout, depth, true );
}

void formatter::format( TextJsonIn &jsin, JsonOut &jsout, int depth, bool force_wrap )
{
    depth++;
    if( jsin.test_array() ) {
        format_collection( jsin, jsout, depth, write_array, force_wrap );
    } else if( jsin.test_object() ) {
        format_collection( jsin, jsout, depth, write_object, force_wrap );
    } else if( jsin.test_string() ) {
        // The string may contain escape sequences which we want to keep in the output.
        const int start_pos = jsin.tell();
        jsin.get_string();
        const int end_pos = jsin.tell();
        std::string str = jsin.substr( start_pos, end_pos - start_pos );
        str = str.substr( str.find( '"' ) );
        str = str.substr( 0, str.rfind( '"' ) + 1 );
        jsout.write_separator();
        *jsout.get_stream() << str;
        jsout.set_need_separator();
    } else if( jsin.test_number() ) {
        // Have to introspect into the string to distinguish integers from floats.
        // Otherwise they won't serialize correctly.
        const int start_pos = jsin.tell();
        jsin.skip_number();
        const int end_pos = jsin.tell();
        std::string str_form = jsin.substr( start_pos, end_pos - start_pos );
        jsin.seek( start_pos );
        if( str_form.find( '.' ) == std::string::npos ) {
            if( str_form.find( '-' ) == std::string::npos ) {
                const uint64_t num = jsin.get_uint64();
                jsout.write( num );
            } else {
                const int64_t num = jsin.get_int64();
                jsout.write( num );
            }
        } else {
            const double num = jsin.get_float();
            // This is QUITE insane, but as far as I can tell there is NO way to configure
            // an ostream to output a float/double meeting two constraints:
            // Always emit a decimal point (and single trailing 0 after a bare decimal point).
            // Don't emit trailing zeroes otherwise.
            std::string double_str = std::to_string( num );
            double_str.erase( double_str.find_last_not_of( '0' ) + 1, std::string::npos );
            if( double_str.back() == '.' ) {
                double_str += "0";
            }
            jsout.write_separator();
            *jsout.get_stream() << double_str;
            jsout.set_need_separator();
        }
    } else if( jsin.test_bool() ) {
        bool tf = jsin.get_bool();
        jsout.write( tf );
    } else if( jsin.test_null() ) {
        jsin.skip_null();
        jsout.write_null();
    } else {
        jsin.skip_value(); // this will throw exception with the invalid element
    }
}