1 //          Copyright Ferdinand Majerech 2014.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 
7 /// Profiling event and its members.
8 module tharsis.prof.event;
9 
10 
11 /** Types of events recorded by Profiler.
12  */
13 // All EventID values must be 5-bit integers, see below.
14 enum EventID: ubyte
15 {
16     // A 'checkpoint' event followed by an absolute time value (8 7-bit bytes).
17     Checkpoint = 0,
18     // Zone start.
19     ZoneStart  = 1,
20     // Zone end.
21     ZoneEnd    = 2,
22     // Info string. EventID/time bytes are followed by a string length byte and a string
23     // of up to 255 chars.
24     Info       = 3,
25 
26     // A variable value. VariableType followed by a 7-bit encoded big-endian variable value.
27     Variable   = 4
28 }
29 
30 /// Variable types that can be recorded.
31 enum VariableType: ubyte
32 {
33     /// int. Encoded as a 5-byte long 7-bit encoded block.
34     Int = 1,
35     /// uint. Encoded as a 5-byte long 7-bit encoded block.
36     Uint = 2,
37     /// float. Encoded as a 5-byte long 7-bit encoded block.
38     Float = 3
39 }
40 
41 // Lengths of various variable types when encoded in 7-bit.
42 package enum variable7BitLengths = [size_t.max, 5, 5, 5];
43 
44 /// Get a VariableType value corresponding to a variable type.
45 VariableType variableType(V)()
46 {
47     static if(is(V == int))       { return VariableType.Int;   }
48     else static if(is(V == uint)) { return VariableType.Uint;  }
49     else static if(is(V == float)){ return VariableType.Float; }
50     else static assert(false, "Unsupported type for Despiker variable event: " ~ V.stringof);
51 }
52 
53 // A global array with all event IDs
54 import std.traits;
55 package immutable allEventIDs = [EnumMembers!EventID];
56 package immutable allVariableTypes = [EnumMembers!VariableType];
57 
58 /// A variable parsed from profile data.
59 struct Variable
60 {
61 package:
62     // Variable type.
63     VariableType type_;
64     union
65     {
66         // Integer value if type_ is VariableType.Int.
67         int int_;
68         // Unsigned integer value if type_ is VariableType.Uint.
69         uint uint_;
70         // Float value if type_ is VariableType.Float.
71         float float_;
72     }
73 
74 public:
75     /// Get the variable type.
76     VariableType type() @safe pure nothrow const @nogc
77     {
78         return type_;
79     }
80 
81     /// toString() with no allocations (except stack).
82     void toString(scope void delegate(const(char)[]) sink) const
83     {
84         char[128] buffer;
85         import core.stdc.stdio; // formattedWrite might be better, maybe rewrite
86         int length;
87         final switch(type_) with(VariableType)
88         {
89             case Int:   length = snprintf(buffer.ptr, buffer.length, "%d", varInt);   break;
90             case Uint:  length = snprintf(buffer.ptr, buffer.length, "%u", varUint);  break;
91             case Float: length = snprintf(buffer.ptr, buffer.length, "%f", varFloat); break;
92         }
93         assert(length > 0 && length < buffer.length, "Error formatting a value to string");
94         sink(buffer[0 .. length]);
95     }
96 
97     /** Get the integer value of the variable
98      *
99      * Can only be called if type is VariableType.Int.
100      */
101     int varInt() @safe pure nothrow const @nogc
102     {
103         assert(type_ == VariableType.Int, "Trying to read a non-int variable as an int");
104         return int_;
105     }
106 
107     /** Get the unsigned integer value of the variable
108      *
109      * Can only be called if type is VariableType.Uint.
110      */
111     uint varUint() @safe pure nothrow const @nogc
112     {
113         assert(type_ == VariableType.Uint, "Trying to read a non-uint variable as a uint");
114         return uint_;
115     }
116 
117     /** Get the float value of the variable
118      *
119      * Can only be called if type is VariableType.Float.
120      */
121     float varFloat() @safe pure nothrow const @nogc
122     {
123         assert(type_ == VariableType.Float, "Trying to read a non-float variable as a float");
124         return float_;
125     }
126 }
127 
128 /// Profiling event generated by EventRange.
129 struct Event
130 {
131     /// Event ID or type.
132     EventID id;
133     /// Time of the event since recording started in hectonanoseconds.
134     ulong time;
135 
136     package union
137     {
138         const(char)[] info_;
139         Variable var_;
140     }
141 
142     /// Get the info string if this is an Info event.
143     const(char)[] info() @trusted pure nothrow const @nogc
144     {
145         assert(id == EventID.Info, "Can't access info if it's not an Info event");
146         return info_;
147     }
148 
149     Variable var() @trusted pure nothrow const @nogc
150     {
151         assert(id == EventID.Variable, "Can't access variable if it's not a Variable event");
152         return var_;
153     }
154 
155     bool opEquals(ref const(Event) rhs) @safe pure nothrow const @nogc 
156     {
157         if(id != rhs.id || time != rhs.time) { return false; }
158         final switch(id)
159         {
160             case EventID.Checkpoint, EventID.ZoneStart, EventID.ZoneEnd:
161                 return true;
162             case EventID.Info:
163                 return info == rhs.info;
164             case EventID.Variable:
165                 if(var.type != rhs.var.type) { return false; }
166                 final switch(var.type)
167                 {
168                     case VariableType.Int:   return var.varInt   == rhs.var.varInt;
169                     case VariableType.Uint:  return var.varUint  == rhs.var.varUint;
170                     case VariableType.Float: return var.varFloat == rhs.var.varFloat;
171                 }
172         }
173     }
174 }