1 
2 module dyaml.yaml_bench;
3 //Benchmark that loads, and optionally extracts data from and/or emits a YAML file. 
4 
5 import std.conv;
6 import std.datetime;
7 import std.stdio;
8 import std.string;
9 import dyaml.all;
10 
11 ///Print help information.
12 void help()
13 {
14     string help =
15         "D:YAML benchmark\n"
16         "Copyright (C) 2011-2014 Ferdinand Majerech\n"
17         "Usage: yaml_bench [OPTION ...] [YAML_FILE]\n"
18         "\n"
19         "Loads and optionally extracts data and/or dumps a YAML file.\n"
20         "\n"
21         "Available options:\n"
22         " -h --help          Show this help information.\n"
23         " -g --get           Extract data from the file (using Node.as()).\n"
24         " -d --dump          Dump the loaded data (to YAML_FILE.dump).\n"
25         " -r --runs=NUM      Repeat parsing the file NUM times.\n"
26         "    --reload        Reload the file from the diskl on every repeat\n"
27         "                    By default, the file is loaded to memory once\n"
28         "                    and repeatedly parsed from memory.\n"
29         " -s --scan-only    Do not execute the entire parsing process, only\n"
30         "                    scanning. Overrides '--dump'.\n";
31     writeln(help);
32 }
33 
34 ///Get data out of every node.
35 void extract(ref Node document)
36 {
37     void crawl(ref Node root)
38     {
39         if(root.isScalar) switch(root.tag)
40         {
41             case "tag:yaml.org,2002:null":      auto value = root.as!YAMLNull;  break;
42             case "tag:yaml.org,2002:bool":      auto value = root.as!bool;      break;
43             case "tag:yaml.org,2002:int":       auto value = root.as!long;      break;
44             case "tag:yaml.org,2002:float":     auto value = root.as!real;      break;
45             case "tag:yaml.org,2002:binary":    auto value = root.as!(ubyte[]); break;
46             case "tag:yaml.org,2002:timestamp": auto value = root.as!SysTime;   break;
47             case "tag:yaml.org,2002:str":       auto value = root.as!string;    break;
48             default: writeln("Unrecognozed tag: ", root.tag);
49         }
50         else if(root.isSequence) foreach(ref Node node; root)
51         {
52             crawl(node);
53         }
54         else if(root.isMapping) foreach(ref Node key, ref Node value; root)
55         {
56             crawl(key);
57             crawl(value);
58         }
59     }
60 
61     crawl(document);
62 }
63 
64 void main(string[] args)
65 {
66     bool get      = false;
67     bool dump     = false;
68     bool reload   = false;
69     bool scanOnly = false;
70     uint runs = 1;
71     string file = null;
72 
73     //Parse command line args
74     foreach(arg; args[1 .. $]) 
75     {
76         auto parts = arg.split("=");
77         if(arg[0] == '-') switch(parts[0]) 
78         {
79             case "--help", "-h":      help(); return;
80             case "--get",  "-g":      get      = true; break;
81             case "--dump", "-d":      dump     = true; break;
82             case "--reload":          reload   = true; break;
83             case "--scan-only", "-s": scanOnly = true; break;
84             case "--runs", "-r":      runs     = parts[1].to!uint; break;
85             default: writeln("\nUnknown argument: ", arg, "\n\n"); help(); return;
86         }
87         else
88         {
89             if(file !is null)
90             {
91                 writeln("\nUnknown argument or file specified twice: ", arg, "\n\n"); 
92                 help(); 
93                 return;
94             }
95 
96             file = arg;
97         }
98     }
99     if(file is null)
100     {
101         writeln("\nFile not specified.\n\n"); 
102         help(); 
103         return;
104     }
105 
106     try
107     {
108         import std.file;
109         void[] fileInMemory;
110         if(!reload) { fileInMemory = std.file.read(file); }
111         void[] fileWorkingCopy = fileInMemory.dup;
112 
113         // Instead of constructing a resolver/constructor with each Loader,
114         // construct them once to remove noise when profiling.
115         auto resolver    = new Resolver();
116         auto constructor = new Constructor();
117 
118         while(runs--)
119         {
120             // Loading the file rewrites the loaded buffer, so if we don't reload from
121             // disk, we need to use a copy of the originally loaded file.
122             if(reload) { fileInMemory = std.file.read(file); }
123             else       { fileWorkingCopy[] = fileInMemory[]; }
124             void[] fileToLoad = reload ? fileInMemory : fileWorkingCopy;
125             if(scanOnly)
126             {
127                 auto loader = Loader(fileToLoad);
128                 loader.scanBench();
129                 continue;
130             }
131             auto loader        = Loader(fileToLoad);
132             loader.resolver    = resolver;
133             loader.constructor = constructor;
134             auto nodes = loader.loadAll();
135             if(dump)
136             {
137                 Dumper(file ~ ".dump").dump(nodes);
138             }
139             if(get) foreach(ref node; nodes)
140             {
141                 extract(node);
142             }
143         }
144     }
145     catch(YAMLException e)
146     {
147         writeln("ERROR: ", e.msg);
148     }
149 }