aboutsummaryrefslogtreecommitdiff
path: root/scripts/json2fbbin/json2fbbin.py
blob: 08b13beec806d574603b512a3d7acdb76b0f9d23 (plain)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
"""Conversion utility from flatbuffer JSON files to binary and the reverse."""
# Copyright (c) 2021-2023, ARM Limited.
# SPDX-License-Identifier: Apache-2.0
import re
from pathlib import Path
from typing import Optional

from runner.run_command import run_sh_command
from runner.run_command import RunShCommandError

MAX_LINE_LEN = 120
MAX_INDENT_LEN = 20


def json_squeeze(json_path: Path):
    """File compression for JSONs, reducing spaces used for number lists."""
    # Move existing file to a new name
    temp_path = json_path.with_suffix(".json_unsqueezed")
    json_path.rename(temp_path)
    # Now read the original file and write a smaller output with less new lines/spaces
    with temp_path.open("r") as tfd:
        with json_path.open("w") as jfd:
            found = False

            for line in tfd:
                # Find lines that are part of number lists
                match = re.match(r"(\s+)(-?[0-9]+),?", line)
                if match:
                    # Found a line with just a number on it (and optional comma)
                    if not found:
                        # New list of numbers
                        numbers = []
                        # Save indent (upto maximum)
                        indent = match.group(1)[0:MAX_INDENT_LEN]
                        found = True
                    numbers.append(match.group(2))
                else:
                    # Found a line without just a number
                    if found:
                        # Format the list of numbers recorded into a concise output
                        # with multiple numbers on a single line, rather than one per line
                        numbers_str = indent
                        for num in numbers:
                            nums = f"{num},"
                            if len(numbers_str) + len(nums) > MAX_LINE_LEN:
                                print(numbers_str, file=jfd)
                                numbers_str = indent
                            numbers_str += nums
                        # print all but the last comma
                        print(numbers_str[:-1], file=jfd)

                        found = False
                    # print the line we just read (that wasn't just a number)
                    print(line, file=jfd, end="")

    # Remove the uncompressed version
    temp_path.unlink()


def fbbin_to_json(
    flatc: Path,
    fbs: Path,
    t_path: Path,
    o_path: Optional[Path] = None,
    squeeze: Optional[bool] = True,
):
    """Convert the binary flatbuffer to JSON.

    flatc: the Path to the flatc compiler program
    fbs: the Path to the fbs (flatbuffer schema) file
    t_path: the Path to the binary flatbuffer file
    o_path: the output Path where JSON file will be put, if None, it is same as t_path
    """
    if o_path is None:
        o_path = t_path.parent
    cmd = [
        str(flatc.absolute()),
        "-o",
        str(o_path.absolute()),
        "--json",
        "--strict-json",
        "--defaults-json",
        "--raw-binary",
        str(fbs.absolute()),
        "--",
        str(t_path.absolute()),
    ]
    run_sh_command(verbose=False, full_cmd=cmd)
    if squeeze:
        json_path = (o_path / t_path.name).with_suffix(".json")
        json_squeeze(json_path)


def json_to_fbbin(flatc: Path, fbs: Path, j_path: Path, o_path: Optional[Path] = None):
    """Convert JSON flatbuffer to binary.

    flatc: the Path to the flatc compiler program
    fbs: the Path to the fbs (flatbuffer schema) file
    j_path: the Path to the JSON flatbuffer file
    o_path: the output Path where JSON file will be put, if None, it is same as j_path
    """
    if o_path is None:
        o_path = j_path.parent
    cmd = [
        str(flatc.absolute()),
        "-o",
        str(o_path.absolute()),
        "--binary",
        str(fbs.absolute()),
        str(j_path.absolute()),
    ]
    run_sh_command(verbose=False, full_cmd=cmd)


# ------------------------------------------------------------------------------


def main(argv=None):
    """Load and convert supplied file based on file suffix."""
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--no-squeeze", action="store_true", help="no compression of json output"
    )
    parser.add_argument(
        "--flatc",
        type=Path,
        default=(
            "reference_model/build/thirdparty/serialization_lib/"
            "third_party/flatbuffers/flatc"
        ),
        help="the path to the flatc compiler program",
    )
    parser.add_argument(
        "--fbs",
        type=Path,
        default="conformance_tests/third_party/serialization_lib/schema/tosa.fbs",
        help="the path to the flatbuffer schema",
    )
    parser.add_argument("path", type=Path, help="the path to the file to convert")
    args = parser.parse_args(argv)
    path = args.path

    if not path.is_file():
        print(f"Invalid file to convert - {path}")
        return 2

    if not args.flatc.is_file():
        print(f"Invalid flatc compiler - {args.flatc}")
        return 2

    if not args.fbs.is_file():
        print(f"Invalid flatbuffer schema - {args.fbs}")
        return 2

    try:
        if path.suffix == ".json":
            json_to_fbbin(args.flatc, args.fbs, path)
        else:
            # Have to assume this is a binary flatbuffer file as could have any suffix
            fbbin_to_json(args.flatc, args.fbs, path, squeeze=(not args.no_squeeze))
    except RunShCommandError as e:
        print(e)
        return 1

    return 0


if __name__ == "__main__":
    exit(main())