switch to new MPL based Fraunhofer FOKUS Public License
[senf.git] / senf / Packets / IntParser.hh
1 // $Id$
2 //
3 // Copyright (C) 2006
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 //
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at 
9 // http://senf.berlios.de/license.html
10 //
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on, 
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
14 //
15 // Software distributed under the License is distributed on an "AS IS" basis, 
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
17 // for the specific language governing rights and limitations under the License.
18 //
19 // The Original Code is Fraunhofer FOKUS code.
20 //
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. 
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
24 //
25 // Contributor(s):
26 //   Stefan Bund <g0dil@berlios.de>
27
28 /** \file
29     \brief IntParser public header */
30
31 #ifndef HH_SENF_Packets_IntParser_
32 #define HH_SENF_Packets_IntParser_ 1
33
34 // Custom includes
35 #include <iostream>
36 #include <boost/cstdint.hpp>
37 #include <boost/static_assert.hpp>
38 #include <boost/integer/integer_mask.hpp>
39 #include "PacketParser.hh"
40
41 //#include "IntParser.mpp"
42 //-/////////////////////////////////////////////////////////////////////////////////////////////////
43 #include "IntParser.ih"
44
45 namespace senf {
46
47     /** \defgroup parseint Integer parsers
48
49         Most packet fields will ultimately contain some type of integral number. The integer parsers
50         allow to parse arbitrary integers in network byte order from 1-32 bit, both signed and
51         unsigned. There are two types of integer parsers:
52
53         \li The normal integer parsers with interpret 1-4 byte integers (9, 16, 24, 32 bits) aligned
54             at byte boundaries.
55         \li The bitfield parsers which parse integers with 1-32 bits aligned at any bit. A special
56             case is the single bit flag parser.
57
58         All fields are parsed in network byte order, the return value of all these parsers is the
59         value in host byte order.
60
61         The interface of all these parsers is the same (p is an arbitrary integer parser instance, v
62         is an integer constant):
63
64         \li <tt>p = v</tt>: Assigns the value to the packet field.
65         \li <tt>p.value(v)</tt>: same as above.
66         \li <tt>p.value()</tt>: Returns the fields value as an integer number.
67         \li Use of p like an integer in most contexts: <tt>p += v</tt>, <tt>p *= v</tt>, <tt>v = p
68             1</tt> and so on. You will only need to use the explicit \c value() member in rare
69             circumstances when the automatic conversion is ambiguous or in some template contexts.
70
71         \ingroup packetparser
72      */
73
74     /** \brief Parse 8bit signed byte aligned integer
75         \see parseint
76         \ingroup parseint
77      */
78     struct Int8Parser
79         : public detail::packet::IntParserOps<Int8Parser,boost::int8_t>,
80           public PacketParserBase
81     {
82         Int8Parser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
83
84         //-////////////////////////////////////////////////////////////////////////
85
86         typedef boost::int8_t value_type;
87         static size_type const fixed_bytes = 1;
88         static value_type const min_value = -128;
89         static value_type const max_value = 127;
90
91         value_type value() const { return i()[0]; }
92         void value(value_type v) { i()[0] = v; }
93         Int8Parser const & operator= (value_type other) { value(other); return *this; }
94     };
95     /** \brief Write parsed value to stream
96         \related Int8Parser
97      */
98     inline std::ostream & operator<<(std::ostream & os, Int8Parser const & i)
99     { os << i.value(); return os; }
100
101     /** \brief Parse 8bit unsigned byte aligned integer
102         \see parseint
103         \ingroup parseint
104      */
105     struct UInt8Parser
106         : public detail::packet::IntParserOps<UInt8Parser,boost::uint8_t>,
107           public PacketParserBase
108     {
109         UInt8Parser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
110
111         //-////////////////////////////////////////////////////////////////////////
112
113         typedef boost::uint8_t value_type;
114         static size_type const fixed_bytes = 1;
115         static value_type const min_value = 0u;
116         static value_type const max_value = 255u;
117
118         value_type value() const { return i()[0]; }
119         void value(value_type v) { i()[0] = v; }
120         UInt8Parser const & operator= (value_type other) { value(other); return *this; }
121     };
122     /** \brief Write parsed value to stream
123         \related UInt8Parser
124      */
125     inline std::ostream & operator<<(std::ostream & os, UInt8Parser const & i)
126     { os << i.value(); return os; }
127
128     /** \brief Parse 16bit signed byte aligned integer
129         \see parseint
130         \ingroup parseint
131      */
132     struct Int16Parser
133         : public detail::packet::IntParserOps<Int16Parser,boost::int16_t>,
134           public PacketParserBase
135     {
136         Int16Parser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
137
138         //-////////////////////////////////////////////////////////////////////////
139
140         typedef boost::int16_t value_type;
141         static size_type const fixed_bytes = 2;
142         static value_type const min_value = -32768;
143         static value_type const max_value = 32767;
144
145
146         value_type value() const { return detail::packet::parse_uint16(i()); }
147         void value(value_type v) { detail::packet::write_uint16(i(),v); }
148         Int16Parser const & operator= (value_type other) { value(other); return *this; }
149     };
150     /** \brief Write parsed value to stream
151         \related Int16Parser
152      */
153     inline std::ostream & operator<<(std::ostream & os, Int16Parser const & i)
154     { os << i.value(); return os; }
155
156     /** \brief Parse 16bit signed byte aligned integer LSB
157         \see parseint
158         \ingroup parseint
159      */
160     struct Int16LSBParser
161         : public detail::packet::IntParserOps<Int16LSBParser,boost::int16_t>,
162           public PacketParserBase
163     {
164         Int16LSBParser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
165
166         //-////////////////////////////////////////////////////////////////////////
167
168         typedef boost::int16_t value_type;
169         static size_type const fixed_bytes = 2;
170         static value_type const min_value = -32768;
171         static value_type const max_value = 32767;
172
173
174         value_type value() const { return detail::packet::parse_uint16LSB(i()); }
175         void value(value_type v) { detail::packet::write_uint16LSB(i(),v); }
176         Int16LSBParser const & operator= (value_type other) { value(other); return *this; }
177     };
178     /** \brief Write parsed value to stream
179         \related Int16LSBParser
180      */
181     inline std::ostream & operator<<(std::ostream & os, Int16LSBParser const & i)
182     { os << i.value(); return os; }
183
184     /** \brief Parse 16bit unsigned byte aligned integer
185         \see parseint
186         \ingroup parseint
187      */
188     struct UInt16Parser
189         : public detail::packet::IntParserOps<UInt16Parser,boost::uint16_t>,
190           public PacketParserBase
191     {
192         UInt16Parser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
193
194         //-////////////////////////////////////////////////////////////////////////
195
196         typedef boost::uint16_t value_type;
197         static size_type const fixed_bytes = 2;
198         static value_type const min_value = 0u;
199         static value_type const max_value = 65535u;
200
201         value_type value() const { return detail::packet::parse_uint16(i()); }
202         void value(value_type v) { detail::packet::write_uint16(i(),v); }
203         UInt16Parser const & operator= (value_type other) { value(other); return *this; }
204     };
205     /** \brief Write parsed value to stream
206         \related UInt16Parser
207      */
208     inline std::ostream & operator<<(std::ostream & os, UInt16Parser const & i)
209     { os << i.value(); return os; }
210
211     /** \brief Parse 16bit unsigned byte aligned integer LSB
212         \see parseint
213         \ingroup parseint
214      */
215     struct UInt16LSBParser
216         : public detail::packet::IntParserOps<UInt16LSBParser,boost::uint16_t>,
217           public PacketParserBase
218     {
219         UInt16LSBParser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
220
221         //-////////////////////////////////////////////////////////////////////////
222
223         typedef boost::uint16_t value_type;
224         static size_type const fixed_bytes = 2;
225         static value_type const min_value = 0u;
226         static value_type const max_value = 65535u;
227
228         value_type value() const { return detail::packet::parse_uint16LSB(i()); }
229         void value(value_type v) { detail::packet::write_uint16LSB(i(),v); }
230         UInt16LSBParser const & operator= (value_type other) { value(other); return *this; }
231     };
232     /** \brief Write parsed value to stream
233         \related UInt16LSBParser
234      */
235     inline std::ostream & operator<<(std::ostream & os, UInt16LSBParser const & i)
236     { os << i.value(); return os; }
237
238     /** \brief Parse 24bit signed byte aligned integer
239         \see parseint
240         \ingroup parseint
241      */
242     struct Int24Parser
243         : public detail::packet::IntParserOps<Int24Parser,boost::int32_t>,
244           public PacketParserBase
245     {
246         Int24Parser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
247
248         //-////////////////////////////////////////////////////////////////////////
249
250         typedef boost::int32_t value_type;
251         static size_type const fixed_bytes = 3;
252         static value_type const min_value = -8388608;
253         static value_type const max_value = 8388607;
254
255         value_type value() const {
256             value_type v (detail::packet::parse_uint24(i())); return v&0x800000 ? v|0xff000000 : v; }
257         void value(value_type v) { detail::packet::write_uint24(i(),v); }
258         Int24Parser const & operator= (value_type other) { value(other); return *this; }
259     };
260     /** \brief Write parsed value to stream
261         \related Int24Parser
262      */
263     inline std::ostream & operator<<(std::ostream & os, Int24Parser const & i)
264     { os << i.value(); return os; }
265
266     /** \brief Parse 24bit unsigned byte aligned integer
267         \see parseint
268         \ingroup parseint
269      */
270     struct UInt24Parser
271         : public detail::packet::IntParserOps<UInt24Parser,boost::uint32_t>,
272           public PacketParserBase
273     {
274         UInt24Parser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
275
276         //-////////////////////////////////////////////////////////////////////////
277
278         typedef boost::uint32_t value_type;
279         static size_type const fixed_bytes = 3;
280         static value_type const min_value = 0u;
281         static value_type const max_value = 16777215u;
282
283         value_type value() const { return detail::packet::parse_uint24(i()); }
284         void value(value_type v) { detail::packet::write_uint24(i(),v); }
285         UInt24Parser const & operator= (value_type other) { value(other); return *this; }
286     };
287     /** \brief Write parsed value to stream
288         \related UInt24Parser
289      */
290     inline std::ostream & operator<<(std::ostream & os, UInt24Parser const & i)
291     { os << i.value(); return os; }
292
293     /** \brief Parse 32bit signed byte aligned integer
294         \see parseint
295         \ingroup parseint
296      */
297     struct Int32Parser
298         : public detail::packet::IntParserOps<Int32Parser,boost::int32_t>,
299           public PacketParserBase
300     {
301         Int32Parser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
302
303         //-////////////////////////////////////////////////////////////////////////
304
305         typedef boost::int32_t value_type;
306         static size_type const fixed_bytes = 4;
307         static value_type const min_value = -2147483647 - 1;
308         static value_type const max_value = 2147483647;
309
310         value_type value() const { return detail::packet::parse_uint32(i()); }
311         void value(value_type v) { detail::packet::write_uint32(i(),v); }
312         Int32Parser const & operator= (value_type other) { value(other); return *this; }
313     };
314     /** \brief Write parsed value to stream
315         \related Int32Parser
316      */
317     inline std::ostream & operator<<(std::ostream & os, Int32Parser const & i)
318     { os << i.value(); return os; }
319
320     /** \brief Parse 32bit unsigned byte aligned integer
321         \see parseint
322         \ingroup parseint
323      */
324     struct UInt32Parser
325         : public detail::packet::IntParserOps<UInt32Parser,boost::uint32_t>,
326           public PacketParserBase
327     {
328         UInt32Parser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
329
330         //-////////////////////////////////////////////////////////////////////////
331
332         typedef boost::uint32_t value_type;
333         static size_type const fixed_bytes = 4;
334         static value_type const min_value = 0u;
335         static value_type const max_value = 4294967295u;
336
337         value_type value() const { return detail::packet::parse_uint32(i()); }
338         void value(value_type v) { detail::packet::write_uint32(i(),v); }
339         UInt32Parser const & operator= (value_type other) { value(other); return *this; }
340     };
341     /** \brief Write parsed value to stream
342         \related UInt32Parser
343      */
344     inline std::ostream & operator<<(std::ostream & os, UInt32Parser const & i)
345     { os << i.value(); return os; }
346
347     struct UInt32LSBParser
348         : public detail::packet::IntParserOps<UInt32LSBParser,boost::uint32_t>,
349           public PacketParserBase
350     {
351         UInt32LSBParser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
352
353         //-////////////////////////////////////////////////////////////////////////
354
355         typedef boost::uint32_t value_type;
356         static size_type const fixed_bytes = 4;
357         static value_type const min_value = 0u;
358         static value_type const max_value = 4294967295u;
359
360         value_type value() const { return detail::packet::parse_uint32LSB(i()); }
361         void value(value_type v) { detail::packet::write_uint32LSB(i(),v); }
362         UInt32LSBParser const & operator= (value_type other) { value(other); return *this; }
363     };
364     /** \brief Write parsed value to stream
365         \related UInt32LSBParser
366      */
367     inline std::ostream & operator<<(std::ostream & os, UInt32LSBParser const & i)
368     { os << i.value(); return os; }
369
370
371
372
373     /** \brief Parse 64bit signed byte aligned integer
374         \see parseint
375         \ingroup parseint
376      */
377     struct Int64Parser
378         : public detail::packet::IntParserOps<Int64Parser,boost::int64_t>,
379           public PacketParserBase
380     {
381         Int64Parser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
382
383         //-////////////////////////////////////////////////////////////////////////
384
385         typedef boost::int64_t value_type;
386         static size_type const fixed_bytes = 8;
387
388         value_type value() const { return detail::packet::parse_uint64(i()); }
389         void value(value_type v) { detail::packet::write_uint64(i(),v); }
390         Int64Parser const & operator= (value_type other) { value(other); return *this; }
391     };
392     /** \brief Write parsed value to stream
393         \related Int64Parser
394     */
395     inline std::ostream & operator<<(std::ostream & os, Int64Parser const & i)
396     { os << i.value(); return os; }
397
398
399     /** \brief Parse 64bit unsigned byte aligned integer
400         \see parseint
401         \ingroup parseint
402      */
403     struct UInt64Parser
404         : public detail::packet::IntParserOps<UInt64Parser,boost::uint64_t>,
405           public PacketParserBase
406     {
407         UInt64Parser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
408
409         //-////////////////////////////////////////////////////////////////////////
410
411         typedef boost::uint64_t value_type;
412         static size_type const fixed_bytes = 8;
413
414         value_type value() const { return detail::packet::parse_uint64(i()); }
415         void value(value_type v) { detail::packet::write_uint64(i(),v); }
416         UInt64Parser const & operator= (value_type other) { value(other); return *this; }
417     };
418     /** \brief Write parsed value to stream
419         \related UInt64Parser
420      */
421     inline std::ostream & operator<<(std::ostream & os, UInt64Parser const & i)
422     { os << i.value(); return os; }
423
424     /** \brief Parse 64bit unsigned byte aligned integer LSB
425         \see parseint
426         \ingroup parseint
427      */
428     struct UInt64LSBParser
429         : public detail::packet::IntParserOps<UInt64LSBParser,boost::uint64_t>,
430           public PacketParserBase
431     {
432         UInt64LSBParser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
433
434         //-////////////////////////////////////////////////////////////////////////
435
436         typedef boost::uint64_t value_type;
437         static size_type const fixed_bytes = 8;
438
439         value_type value() const { return detail::packet::parse_uint64LSB(i()); }
440         void value(value_type v) { detail::packet::write_uint64LSB(i(),v); }
441         UInt64LSBParser const & operator= (value_type other) { value(other); return *this; }
442     };
443     /** \brief Write parsed value to stream
444         \related UInt64LSBParser
445      */
446     inline std::ostream & operator<<(std::ostream & os, UInt64LSBParser const & i)
447     { os << i.value(); return os; }
448
449     /** \brief Parse signed bitfield with up to 32bit's
450
451         This parser will parse a bitfield beginning at the bit \a Start and ending \e before \a
452         End. Bits are numbered <em>most significant bit first</em> as this is the customary
453         numbering used when defining packet data structures. \a Start and \a End can be \e
454         arbitrary as long as the field is between 1 and 32 bits in size. In other words, \c
455         IntFieldParser<53,81> is a valid 30 bit field.
456
457         When defining a compound parser with several bit fields, you need to take care of the fact,
458         that several integer field parsers will interpret the same data \e bytes (but not the same
459         \e bits). It is customary for several integer field parsers to start at the same byte offset
460         with ever increasing bit offsets.
461
462         \see parseint
463
464         \implementation The integer field parser is highly optimized. Since the bit positions are
465             compile-time constants, the compiler will create optimized bit-masks to directly access
466             the value. The parser is also optimized to access the minimum number of data bytes
467             necessary.
468
469         \ingroup parseint
470      */
471     template <unsigned Start, unsigned End>
472     struct IntFieldParser
473         : public detail::packet::IntParserOps<IntFieldParser<Start,End>,boost::int32_t>,
474           public PacketParserBase
475     {
476         IntFieldParser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
477
478         //-////////////////////////////////////////////////////////////////////////
479
480         typedef boost::int32_t value_type;
481         static size_type const start_bit = Start;
482         static size_type const end_bit = End;
483         static size_type const fixed_bytes = (End-1)/8+1;
484         static value_type const max_value = boost::low_bits_mask_t<End-Start-1>::sig_bits;
485         static value_type const min_value = - max_value - 1;
486
487
488         value_type value() const {
489             value_type v (detail::packet::parse_bitfield<Start,End>::parse(i()));
490             return v&boost::high_bit_mask_t<End-Start-1>::high_bit ?
491                 v | ~boost::low_bits_mask_t<End-Start>::sig_bits : v;
492         }
493         void value(value_type v) { detail::packet::parse_bitfield<Start,End>::write(i(),v); }
494         IntFieldParser const & operator= (value_type other) { value(other); return *this; }
495
496     private:
497         BOOST_STATIC_ASSERT( Start<End );
498         BOOST_STATIC_ASSERT( End-Start<=32 );
499     };
500     /** \brief Write parsed value to stream
501         \related IntFieldParser
502      */
503     template <unsigned Start, unsigned End>
504     inline std::ostream & operator<<(std::ostream & os, IntFieldParser<Start,End> const & i)
505     { os << i.value(); return os; }
506
507     /** \brief Parse unsigned bitfield with up to 32bit's
508
509         This parser will parse a bitfield beginning at the bit \a Start and ending \e before \a
510         End. Bits are numbered <em>most significant bit first</em> as this is the customary
511         numbering used when defining packet data structures. \a Start and \a End can be \e
512         arbitrary as long as the field is between 1 and 32 bits in size. In other words, \c
513         IntFieldParser<53,81> is a valid 30 bit field.
514
515         When defining a compound parser with several bit fields, you need to take care of the fact,
516         that several integer field parsers will interpret the same data \e bytes (but not the same
517         \e bits). It is customary for several integer field parsers to start at the same byte offset
518         with ever increasing bit offsets.
519
520         \see parseint
521
522         \implementation The integer field parser is highly optimized. Since the bit positions are
523             compile-time constants, the compiler will create optimized bit-masks to directly access
524             the value. The parser is also optimized to access the minimum number of data bytes
525             necessary.
526
527         \ingroup parseint
528      */
529     template <unsigned Start, unsigned End>
530     struct UIntFieldParser
531         : public detail::packet::IntParserOps<UIntFieldParser<Start,End>,boost::uint32_t>,
532           public PacketParserBase
533     {
534         UIntFieldParser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
535
536         //-////////////////////////////////////////////////////////////////////////
537
538         typedef boost::uint32_t value_type;
539         static size_type const start_bit = Start;
540         static size_type const end_bit = End;
541         static size_type const fixed_bytes = (End-1)/8+1;
542         static value_type const min_value = 0u;
543         static value_type const max_value = boost::low_bits_mask_t<End-Start>::sig_bits;
544
545         value_type value() const { return detail::packet::parse_bitfield<Start,End>::parse(i()); }
546         void value(value_type v) { detail::packet::parse_bitfield<Start,End>::write(i(),v); }
547         UIntFieldParser const & operator= (value_type other) { value(other); return *this; }
548
549     private:
550         BOOST_STATIC_ASSERT( Start<End );
551         BOOST_STATIC_ASSERT( End-Start<=32 );
552     };
553     /** \brief Write parsed value to stream
554         \related UIntFieldParser
555      */
556     template <unsigned Start, unsigned End>
557     inline std::ostream & operator<<(std::ostream & os, UIntFieldParser<Start,End> const & i)
558     { os << i.value(); return os; }
559
560     /** \brief Parse single-bit flag
561
562         This parser will parse a single bit as True/False value. Bits are numbered <em>most
563         significant bit first</em> as this is the customary numbering used when defining packet data
564         structures. \a Bit can be arbitrary, \c FlagParser<75> is a valid flag parser.
565
566         When defining a compound parser with several bit fields, you need to take care of the fact,
567         that several integer field parsers will interpret the same data \e bytes (but not the same
568         \e bits). It is customary for several integer field parsers to start at the same byte offset
569         with ever increasing bit offsets.
570
571         \see parseint
572         \ingroup parseint
573      */
574     template <unsigned Bit>
575     struct FlagParser
576         : public detail::packet::IntParserOps<FlagParser<Bit>,bool>,
577           public PacketParserBase
578     {
579         FlagParser(data_iterator i, state_type s) : PacketParserBase(i,s,fixed_bytes) {}
580
581         //-////////////////////////////////////////////////////////////////////////
582
583         typedef bool value_type;
584         static size_type const bit = Bit;
585         static size_type const fixed_bytes = Bit/8+1;
586         static value_type const min_value = 0;
587         static value_type const max_value = 1;
588
589         value_type value() const { return i()[Bit/8] & (1<<(7-(Bit%8))); }
590         void value(value_type v) {
591             if (v) i()[Bit/8] |= 1<<(7-(Bit%8));
592             else   i()[Bit/8] &= ~(1<<(7-(Bit%8)));
593         }
594         FlagParser const & operator= (value_type other) { value(other); return *this; }
595     };
596     /** \brief Write parsed value to stream
597         \related FlagParser
598      */
599     template <unsigned Bit>
600     inline std::ostream & operator<<(std::ostream & os, FlagParser<Bit> const & i)
601     { os << i.value(); return os; }
602
603 }
604
605 //-/////////////////////////////////////////////////////////////////////////////////////////////////
606 #endif
607 #if !defined(HH_SENF_Packets_Packets__decls_) && !defined(HH_SENF_Packets_IntParser_i_)
608 #define HH_SENF_Packets_IntParser_i_
609 //#include "IntParser.cci"
610 //#include "IntParser.ct"
611 //#include "IntParser.cti"
612 #endif
613
614 \f
615 // Local Variables:
616 // mode: c++
617 // fill-column: 100
618 // c-file-style: "senf"
619 // indent-tabs-mode: nil
620 // ispell-local-dictionary: "american"
621 // compile-command: "scons -u test"
622 // comment-column: 40
623 // End: