aboutsummaryrefslogtreecommitdiffstats
path: root/libtransport/src/hicn/transport/utils/log.cc
blob: 00bd7d54f9134a5145d54b4b219b0e40a62c549d (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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
/*
 * Copyright (c) 2017-2019 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 wonder-mice
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/* When defined, Android log (android/log.h) will be used by default instead of
 * stderr (ignored on non-Android platforms). Date, time, pid and tid (context)
 * will be provided by Android log. Android log features will be used to output
 * log level and tag.
 */
#ifdef TRANSPORT_LOG_USE_ANDROID_LOG
#undef TRANSPORT_LOG_USE_ANDROID_LOG
#if defined(__ANDROID__)
#define TRANSPORT_LOG_USE_ANDROID_LOG 1
#else
#define TRANSPORT_LOG_USE_ANDROID_LOG 0
#endif
#else
#define TRANSPORT_LOG_USE_ANDROID_LOG 0
#endif
/* When defined, NSLog (uses Apple System Log) will be used instead of stderr
 * (ignored on non-Apple platforms). Date, time, pid and tid (context) will be
 * provided by NSLog. Curiously, doesn't use NSLog() directly, but piggybacks on
 * non-public CFLog() function. Both use Apple System Log internally, but it's
 * easier to call CFLog() from C than NSLog(). Current implementation doesn't
 * support "%@" format specifier.
 */
#ifdef TRANSPORT_LOG_USE_NSLOG
#undef TRANSPORT_LOG_USE_NSLOG
#if defined(__APPLE__) && defined(__MACH__)
#define TRANSPORT_LOG_USE_NSLOG 1
#else
#define TRANSPORT_LOG_USE_NSLOG 0
#endif
#else
#define TRANSPORT_LOG_USE_NSLOG 0
#endif
/* When defined, OutputDebugString() will be used instead of stderr (ignored on
 * non-Windows platforms). Uses OutputDebugStringA() variant and feeds it with
 * UTF-8 data.
 */
#ifdef TRANSPORT_LOG_USE_DEBUGSTRING
#undef TRANSPORT_LOG_USE_DEBUGSTRING
#if defined(_WIN32) || defined(_WIN64)
#define TRANSPORT_LOG_USE_DEBUGSTRING 1
#else
#define TRANSPORT_LOG_USE_DEBUGSTRING 0
#endif
#else
#define TRANSPORT_LOG_USE_DEBUGSTRING 0
#endif
/* When defined, TRANSPORT_LOG library will not contain definition of tag prefix
 * variable. In that case it must be defined elsewhere using
 * TRANSPORT_LOG_DEFINE_TAG_PREFIX macro, for example:
 *
 *   TRANSPORT_LOG_DEFINE_TAG_PREFIX = "ProcessName";
 *
 * This allows to specify custom value for static initialization and avoid
 * overhead of setting this value in runtime.
 */
#ifdef TRANSPORT_LOG_EXTERN_TAG_PREFIX
#undef TRANSPORT_LOG_EXTERN_TAG_PREFIX
#define TRANSPORT_LOG_EXTERN_TAG_PREFIX 1
#else
#define TRANSPORT_LOG_EXTERN_TAG_PREFIX 0
#endif
/* When defined, TRANSPORT_LOG library will not contain definition of global
 * format variable. In that case it must be defined elsewhere using
 * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT macro, for example:
 *
 *   TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {MEM_WIDTH};
 *
 * This allows to specify custom value for static initialization and avoid
 * overhead of setting this value in runtime.
 */
#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT
#undef TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT
#define TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT 1
#else
#define TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT 0
#endif
/* When defined, transport_log library will not contain definition of global
 * output variable. In that case it must be defined elsewhere using
 * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT macro, for example:
 *
 *   TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_PUT_STD,
 * custom_output_callback};
 *
 * This allows to specify custom value for static initialization and avoid
 * overhead of setting this value in runtime.
 */
#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT
#undef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT
#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT 1
#else
#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT 0
#endif
/* When defined, transport_log library will not contain definition of global
 * output level variable. In that case it must be defined elsewhere using
 * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL macro, for example:
 *
 *   TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = TRANSPORT_LOG_WARN;
 *
 * This allows to specify custom value for static initialization and avoid
 * overhead of setting this value in runtime.
 */
#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
#undef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 1
#else
#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 0
#endif
/* When defined, implementation will prefer smaller code size over speed.
 * Very rough estimate is that code will be up to 2x smaller and up to 2x
 * slower. Disabled by default.
 */
#ifdef TRANSPORT_LOG_OPTIMIZE_SIZE
#undef TRANSPORT_LOG_OPTIMIZE_SIZE
#define TRANSPORT_LOG_OPTIMIZE_SIZE 1
#else
#define TRANSPORT_LOG_OPTIMIZE_SIZE 0
#endif
/* Size of the log line buffer. The buffer is allocated on stack. It limits
 * maximum length of a log line.
 */
#ifndef TRANSPORT_LOG_BUF_SZ
#define TRANSPORT_LOG_BUF_SZ 512
#endif
/* Default number of bytes in one line of memory output. For large values
 * TRANSPORT_LOG_BUF_SZ also must be increased.
 */
#ifndef TRANSPORT_LOG_MEM_WIDTH
#define TRANSPORT_LOG_MEM_WIDTH 32
#endif
/* String to put in the end of each log line (can be empty). Its value used by
 * stderr output callback. Its size used as a default value for
 * TRANSPORT_LOG_EOL_SZ.
 */
#ifndef TRANSPORT_LOG_EOL
#define TRANSPORT_LOG_EOL "\n"
#endif
/* Default delimiter that separates parts of log message. Can NOT contain '%'
 * or '\0'.
 *
 * Log message format specifications can override (or ignore) this value. For
 * more details see TRANSPORT_LOG_MESSAGE_CTX_FORMAT,
 * TRANSPORT_LOG_MESSAGE_SRC_FORMAT and TRANSPORT_LOG_MESSAGE_TAG_FORMAT.
 */
#ifndef TRANSPORT_LOG_DEF_DELIMITER
#define TRANSPORT_LOG_DEF_DELIMITER " "
#endif
/* Specifies log message context format. Log message context includes date,
 * time, process id, thread id and message's log level. Custom information can
 * be added as well. Supported fields: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND,
 * MILLISECOND, PID, TID, LEVEL, S(str), F_INIT(statements),
 * F_UINT(width, value).
 *
 * Must be defined as a tuple, for example:
 *
 *   #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT (YEAR, S("."), MONTH, S("."), DAY,
 * S(" > "))
 *
 * In that case, resulting log message will be:
 *
 *   2016.12.22 > TAG function@filename.c:line Message text
 *
 * Note, that tag, source location and message text are not impacted by
 * this setting. See TRANSPORT_LOG_MESSAGE_TAG_FORMAT and
 * TRANSPORT_LOG_MESSAGE_SRC_FORMAT.
 *
 * If message context must be visually separated from the rest of the message,
 * it must be reflected in context format (notice trailing S(" > ") in the
 * example above).
 *
 * S(str) adds constant string str. String can NOT contain '%' or '\0'.
 *
 * F_INIT(statements) adds initialization statement(s) that will be evaluated
 * once for each log message. All statements are evaluated in specified order.
 * Several F_INIT() fields can be used in every log message format
 * specification. Fields, like F_UINT(width, value), are allowed to use results
 * of initialization statements. If statement introduces variables (or other
 * names, like structures) they must be prefixed with "f_". Statements  must be
 * enclosed into additional "()". Example:
 *
 *   #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \
 *       (F_INIT(( struct rusage f_ru; getrusage(RUSAGE_SELF, &f_ru); )), \
 *        YEAR, S("."), MONTH, S("."), DAY, S(" "), \
 *        F_UINT(5, f_ru.ru_nsignals), \
 *        S(" "))
 *
 * F_UINT(width, value) adds unsigned integer value extended with up to width
 * spaces (for alignment purposes). Value can be any expression that evaluates
 * to unsigned integer. If expression contains non-standard functions, they
 * must be declared with F_INIT(). Example:
 *
 *   #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \
 *        (YEAR, S("."), MONTH, S("."), DAY, S(" "), \
 *        F_INIT(( unsigned tickcount(); )), \
 *        F_UINT(5, tickcount()), \
 *        S(" "))
 *
 * Other log message format specifications follow same rules, but have a
 * different set of supported fields.
 */
#ifndef TRANSPORT_LOG_MESSAGE_CTX_FORMAT
#define TRANSPORT_LOG_MESSAGE_CTX_FORMAT                                       \
  (MONTH, S("-"), DAY, S(TRANSPORT_LOG_DEF_DELIMITER), HOUR, S(":"), MINUTE,   \
   S(":"), SECOND, S("."), MILLISECOND, S(TRANSPORT_LOG_DEF_DELIMITER), PID,   \
   S(TRANSPORT_LOG_DEF_DELIMITER), TID, S(TRANSPORT_LOG_DEF_DELIMITER), LEVEL, \
   S(TRANSPORT_LOG_DEF_DELIMITER))
#endif
/* Example:
 */
/* Specifies log message tag format. It includes tag prefix and tag. Custom
 * information can be added as well. Supported fields:
 * TAG(prefix_delimiter, tag_delimiter), S(str), F_INIT(statements),
 * F_UINT(width, value).
 *
 * TAG(prefix_delimiter, tag_delimiter) adds following string to log message:
 *
 *   PREFIX<prefix_delimiter>TAG<tag_delimiter>
 *
 * Prefix delimiter will be used only when prefix is not empty. Tag delimiter
 * will be used only when prefixed tag is not empty. Example:
 *
 *   #define TRANSPORT_LOG_TAG_FORMAT (S("["), TAG(".", ""), S("] "))
 *
 * See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details.
 */
#ifndef TRANSPORT_LOG_MESSAGE_TAG_FORMAT
#define TRANSPORT_LOG_MESSAGE_TAG_FORMAT (TAG(".", TRANSPORT_LOG_DEF_DELIMITER))
#endif
/* Specifies log message source location format. It includes function name,
 * file name and file line. Custom information can be added as well. Supported
 * fields: FUNCTION, FILENAME, FILELINE, S(str), F_INIT(statements),
 * F_UINT(width, value).
 *
 * See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details.
 */
#ifndef TRANSPORT_LOG_MESSAGE_SRC_FORMAT
#define TRANSPORT_LOG_MESSAGE_SRC_FORMAT \
  (FUNCTION, S("@"), FILENAME, S(":"), FILELINE, S(TRANSPORT_LOG_DEF_DELIMITER))
#endif
/* Fields that can be used in log message format specifications (see above).
 * Mentioning them here explicitly, so we know that nobody else defined them
 * before us. See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details.
 */
#define YEAR YEAR
#define MONTH MONTH
#define DAY DAY
#define MINUTE MINUTE
#define SECOND SECOND
#define MILLISECOND MILLISECOND
#define PID PID
#define TID TID
#define LEVEL LEVEL
#define TAG(prefix_delim, tag_delim) TAG(prefix_delim, tag_delim)
#define FUNCTION FUNCTION
#define FILENAME FILENAME
#define FILELINE FILELINE
#define S(str) S(str)
#define F_INIT(statements) F_INIT(statements)
#define F_UINT(width, value) F_UINT(width, value)
/* Number of bytes to reserve for EOL in the log line buffer (must be >0).
 * Must be larger than or equal to length of TRANSPORT_LOG_EOL with terminating
 * null.
 */
#ifndef TRANSPORT_LOG_EOL_SZ
#define TRANSPORT_LOG_EOL_SZ sizeof(TRANSPORT_LOG_EOL)
#endif
/* Compile instrumented version of the library to facilitate unit testing.
 */
#ifndef TRANSPORT_LOG_INSTRUMENTED
#define TRANSPORT_LOG_INSTRUMENTED 0
#endif

#if defined(__linux__)
#if !defined(__ANDROID__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#endif
#if defined(__MINGW32__)
#ifdef __STRICT_ANSI__
#undef __STRICT_ANSI__
#endif
#endif

#include <assert.h>
#include <ctype.h>
#include <hicn/transport/utils/log.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#else
#include <sys/time.h>
#include <unistd.h>
#if defined(__linux__)
#include <linux/limits.h>
#else
#include <sys/syslimits.h>
#endif
#endif

#if defined(__linux__)
#include <sys/prctl.h>
#include <sys/types.h>
#if !defined(__ANDROID__)
#include <sys/syscall.h>
#endif
#endif
#if defined(__MACH__)
#include <pthread.h>
#endif

#define INLINE _TRANSPORT_LOG_INLINE
#define VAR_UNUSED(var) (void)var
#define RETVAL_UNUSED(expr) \
  do {                      \
    while (expr) break;     \
  } while (0)
#define STATIC_ASSERT(name, cond) typedef char assert_##name[(cond) ? 1 : -1]
#define ASSERT_UNREACHABLE(why) assert(!sizeof(why))
#ifndef _countof
#define _countof(xs) (sizeof(xs) / sizeof((xs)[0]))
#endif

#if TRANSPORT_LOG_INSTRUMENTED
#define INSTRUMENTED_CONST
#else
#define INSTRUMENTED_CONST const
#endif

#define _PP_PASTE_2(a, b) a##b
#define _PP_CONCAT_2(a, b) _PP_PASTE_2(a, b)

#define _PP_PASTE_3(a, b, c) a##b##c
#define _PP_CONCAT_3(a, b, c) _PP_PASTE_3(a, b, c)

/* Microsoft C preprocessor is a piece of shit. This moron treats __VA_ARGS__
 * as a single token and requires additional expansion to realize that it's
 * actually a list. If not for it, there would be no need in this extra
 * expansion.
 */
#define _PP_ID(x) x
#define _PP_NARGS_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
                    _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, \
                    _24, ...)                                              \
  _24
#define _PP_NARGS(...)                                                        \
  _PP_ID(_PP_NARGS_N(__VA_ARGS__, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, \
                     13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))

/* There is a more efficient way to implement this, but it requires
 * working C preprocessor. Unfortunately, Microsoft Visual Studio doesn't
 * have one.
 */
#define _PP_HEAD__(x, ...) x
#define _PP_HEAD_(...) _PP_ID(_PP_HEAD__(__VA_ARGS__, ~))
#define _PP_HEAD(xs) _PP_HEAD_ xs
#define _PP_TAIL_(x, ...) (__VA_ARGS__)
#define _PP_TAIL(xs) _PP_TAIL_ xs
#define _PP_UNTUPLE_(...) __VA_ARGS__
#define _PP_UNTUPLE(xs) _PP_UNTUPLE_ xs

/* Apply function macro to each element in tuple. Output is not
 * enforced to be a tuple.
 */
#define _PP_MAP_1(f, xs) f(_PP_HEAD(xs))
#define _PP_MAP_2(f, xs) f(_PP_HEAD(xs)) _PP_MAP_1(f, _PP_TAIL(xs))
#define _PP_MAP_3(f, xs) f(_PP_HEAD(xs)) _PP_MAP_2(f, _PP_TAIL(xs))
#define _PP_MAP_4(f, xs) f(_PP_HEAD(xs)) _PP_MAP_3(f, _PP_TAIL(xs))
#define _PP_MAP_5(f, xs) f(_PP_HEAD(xs)) _PP_MAP_4(f, _PP_TAIL(xs))
#define _PP_MAP_6(f, xs) f(_PP_HEAD(xs)) _PP_MAP_5(f, _PP_TAIL(xs))
#define _PP_MAP_7(f, xs) f(_PP_HEAD(xs)) _PP_MAP_6(f, _PP_TAIL(xs))
#define _PP_MAP_8(f, xs) f(_PP_HEAD(xs)) _PP_MAP_7(f, _PP_TAIL(xs))
#define _PP_MAP_9(f, xs) f(_PP_HEAD(xs)) _PP_MAP_8(f, _PP_TAIL(xs))
#define _PP_MAP_10(f, xs) f(_PP_HEAD(xs)) _PP_MAP_9(f, _PP_TAIL(xs))
#define _PP_MAP_11(f, xs) f(_PP_HEAD(xs)) _PP_MAP_10(f, _PP_TAIL(xs))
#define _PP_MAP_12(f, xs) f(_PP_HEAD(xs)) _PP_MAP_11(f, _PP_TAIL(xs))
#define _PP_MAP_13(f, xs) f(_PP_HEAD(xs)) _PP_MAP_12(f, _PP_TAIL(xs))
#define _PP_MAP_14(f, xs) f(_PP_HEAD(xs)) _PP_MAP_13(f, _PP_TAIL(xs))
#define _PP_MAP_15(f, xs) f(_PP_HEAD(xs)) _PP_MAP_14(f, _PP_TAIL(xs))
#define _PP_MAP_16(f, xs) f(_PP_HEAD(xs)) _PP_MAP_15(f, _PP_TAIL(xs))
#define _PP_MAP_17(f, xs) f(_PP_HEAD(xs)) _PP_MAP_16(f, _PP_TAIL(xs))
#define _PP_MAP_18(f, xs) f(_PP_HEAD(xs)) _PP_MAP_17(f, _PP_TAIL(xs))
#define _PP_MAP_19(f, xs) f(_PP_HEAD(xs)) _PP_MAP_18(f, _PP_TAIL(xs))
#define _PP_MAP_20(f, xs) f(_PP_HEAD(xs)) _PP_MAP_19(f, _PP_TAIL(xs))
#define _PP_MAP_21(f, xs) f(_PP_HEAD(xs)) _PP_MAP_20(f, _PP_TAIL(xs))
#define _PP_MAP_22(f, xs) f(_PP_HEAD(xs)) _PP_MAP_21(f, _PP_TAIL(xs))
#define _PP_MAP_23(f, xs) f(_PP_HEAD(xs)) _PP_MAP_22(f, _PP_TAIL(xs))
#define _PP_MAP_24(f, xs) f(_PP_HEAD(xs)) _PP_MAP_23(f, _PP_TAIL(xs))
#define _PP_MAP(f, xs) _PP_CONCAT_2(_PP_MAP_, _PP_NARGS xs)(f, xs)

/* Apply function macro to each element in tuple in reverse order.
 * Output is not enforced to be a tuple.
 */
#define _PP_RMAP_1(f, xs) f(_PP_HEAD(xs))
#define _PP_RMAP_2(f, xs) _PP_RMAP_1(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_3(f, xs) _PP_RMAP_2(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_4(f, xs) _PP_RMAP_3(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_5(f, xs) _PP_RMAP_4(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_6(f, xs) _PP_RMAP_5(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_7(f, xs) _PP_RMAP_6(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_8(f, xs) _PP_RMAP_7(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_9(f, xs) _PP_RMAP_8(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_10(f, xs) _PP_RMAP_9(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_11(f, xs) _PP_RMAP_10(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_12(f, xs) _PP_RMAP_11(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_13(f, xs) _PP_RMAP_12(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_14(f, xs) _PP_RMAP_13(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_15(f, xs) _PP_RMAP_14(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_16(f, xs) _PP_RMAP_15(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_17(f, xs) _PP_RMAP_16(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_18(f, xs) _PP_RMAP_17(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_19(f, xs) _PP_RMAP_18(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_20(f, xs) _PP_RMAP_19(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_21(f, xs) _PP_RMAP_20(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_22(f, xs) _PP_RMAP_21(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_23(f, xs) _PP_RMAP_22(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP_24(f, xs) _PP_RMAP_23(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
#define _PP_RMAP(f, xs) _PP_CONCAT_2(_PP_RMAP_, _PP_NARGS xs)(f, xs)

/* Used to implement _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS() macro. All
 * possible fields must be mentioned here. Not counting F_INIT() here because
 * it's somewhat special and is handled spearatly (at least for now).
 */
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__ (0 << 0)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__YEAR (1 << 1)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MONTH (1 << 2)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__DAY (1 << 3)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__HOUR (1 << 4)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MINUTE (1 << 5)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__SECOND (1 << 6)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MILLISECOND (1 << 7)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__PID (1 << 8)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__TID (1 << 9)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__LEVEL (1 << 10)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__TAG(ps, ts) (1 << 11)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FUNCTION (1 << 12)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FILENAME (1 << 13)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FILELINE (1 << 14)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__S(s) (1 << 15)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__F_INIT(expr) (0 << 16)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__F_UINT(w, v) (1 << 17)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK(field) \
  _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_MASK_, _, field)

/* Logical "or" of masks of fields used in specified format specification.
 */
#define _TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(format) \
  (0 _PP_MAP(| _TRANSPORT_LOG_MESSAGE_FORMAT_MASK, format))

/* Expands to expressions that evaluates to true if field is used in
 * specified format specification. Example:
 *
 *   #if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(F_UINT,
 * TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
 *       ...
 *   #endif
 */
#define _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, format) \
  (_TRANSPORT_LOG_MESSAGE_FORMAT_MASK(field) &                \
   _TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(format))

/* Same, but checks all supported format specifications.
 */
#define _TRANSPORT_LOG_MESSAGE_FORMAT_FIELD_USED(field)                        \
  (_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field,                               \
                                          TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
   _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field,                               \
                                          TRANSPORT_LOG_MESSAGE_TAG_FORMAT) || \
   _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field,                               \
                                          TRANSPORT_LOG_MESSAGE_SRC_FORMAT))

#define _TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED                            \
  (_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(YEAR,                                \
                                          TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
   _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MONTH,                               \
                                          TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
   _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(DAY,                                 \
                                          TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
   _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(HOUR,                                \
                                          TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
   _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MINUTE,                              \
                                          TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
   _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(SECOND,                              \
                                          TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
   _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MILLISECOND,                         \
                                          TRANSPORT_LOG_MESSAGE_CTX_FORMAT))

#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
#pragma warning(disable : 4204) /* nonstandard extension used: non-constant \
                                   aggregate initializer */
#define memccpy _memccpy
#endif

#if (defined(_MSC_VER) && !defined(__INTEL_COMPILER)) || defined(__MINGW64__)
#define vsnprintf(s, sz, fmt, va) fake_vsnprintf(s, sz, fmt, va)
static int fake_vsnprintf(char *s, size_t sz, const char *fmt, va_list ap) {
  const int n = vsnprintf_s(s, sz, _TRUNCATE, fmt, ap);
  return 0 < n ? n : (int)sz + 1; /* no need in _vscprintf() for now */
}
#if TRANSPORT_LOG_OPTIMIZE_SIZE
#define snprintf(s, sz, ...) fake_snprintf(s, sz, __VA_ARGS__)
static int fake_snprintf(char *s, size_t sz, const char *fmt, ...) {
  va_list va;
  va_start(va, fmt);
  const int n = fake_vsnprintf(s, sz, fmt, va);
  va_end(va);
  return n;
}
#endif
#endif

typedef void (*time_cb)(struct tm *const tm, unsigned *const usec);
typedef void (*pid_cb)(int *const pid, int *const tid);
typedef void (*buffer_cb)(transport_log_message *msg, char *buf);

typedef struct src_location {
  const char *const func;
  const char *const file;
  const unsigned line;
} src_location;

typedef struct mem_block {
  const void *const d;
  const unsigned d_sz;
} mem_block;

static void time_callback(struct tm *const tm, unsigned *const usec);
static void pid_callback(int *const pid, int *const tid);
static void buffer_callback(transport_log_message *msg, char *buf);

STATIC_ASSERT(eol_fits_eol_sz,
              sizeof(TRANSPORT_LOG_EOL) <= TRANSPORT_LOG_EOL_SZ);
STATIC_ASSERT(eol_sz_greater_than_zero, 0 < TRANSPORT_LOG_EOL_SZ);
STATIC_ASSERT(eol_sz_less_than_buf_sz,
              TRANSPORT_LOG_EOL_SZ < TRANSPORT_LOG_BUF_SZ);
#if !defined(_WIN32) && !defined(_WIN64)
STATIC_ASSERT(buf_sz_less_than_pipe_buf, TRANSPORT_LOG_BUF_SZ <= PIPE_BUF);
#endif
static const char c_hex[] = "0123456789abcdef";

static INSTRUMENTED_CONST unsigned g_buf_sz =
    TRANSPORT_LOG_BUF_SZ - TRANSPORT_LOG_EOL_SZ;
static INSTRUMENTED_CONST time_cb g_time_cb = time_callback;
static INSTRUMENTED_CONST pid_cb g_pid_cb = pid_callback;
static INSTRUMENTED_CONST buffer_cb g_buffer_cb = buffer_callback;

#if TRANSPORT_LOG_USE_ANDROID_LOG
#include <android/log.h>

static INLINE int android_lvl(const int lvl) {
  switch (lvl) {
    case TRANSPORT_LOG_VERBOSE:
      return ANDROID_LOG_VERBOSE;
    case TRANSPORT_LOG_DEBUG:
      return ANDROID_LOG_DEBUG;
    case TRANSPORT_LOG_INFO:
      return ANDROID_LOG_INFO;
    case TRANSPORT_LOG_WARN:
      return ANDROID_LOG_WARN;
    case TRANSPORT_LOG_ERROR:
      return ANDROID_LOG_ERROR;
    case TRANSPORT_LOG_FATAL:
      return ANDROID_LOG_FATAL;
    default:
      ASSERT_UNREACHABLE("Bad log level");
      return ANDROID_LOG_UNKNOWN;
  }
}

static void out_android_callback(const transport_log_message *const msg,
                                 void *arg) {
  VAR_UNUSED(arg);
  *msg->p = 0;
  const char *tag = msg->p;
  if (msg->tag_e != msg->tag_b) {
    tag = msg->tag_b;
    *msg->tag_e = 0;
  }
  __android_log_print(android_lvl(msg->lvl), tag, "%s", msg->msg_b);
}

enum { OUT_ANDROID_MASK = TRANSPORT_LOG_PUT_STD & ~TRANSPORT_LOG_PUT_CTX };
#define OUT_ANDROID OUT_ANDROID_MASK, 0, out_android_callback
#endif

#if TRANSPORT_LOG_USE_NSLOG
#include <CoreFoundation/CoreFoundation.h>
CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...);

static INLINE int apple_lvl(const int lvl) {
  switch (lvl) {
    case TRANSPORT_LOG_VERBOSE:
      return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */
      ;
    case TRANSPORT_LOG_DEBUG:
      return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */
      ;
    case TRANSPORT_LOG_INFO:
      return 6; /* ASL_LEVEL_INFO / kCFLogLevelInfo */
      ;
    case TRANSPORT_LOG_WARN:
      return 4; /* ASL_LEVEL_WARNING / kCFLogLevelWarning */
      ;
    case TRANSPORT_LOG_ERROR:
      return 3; /* ASL_LEVEL_ERR / kCFLogLevelError */
      ;
    case TRANSPORT_LOG_FATAL:
      return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */
      ;
    default:
      ASSERT_UNREACHABLE("Bad log level");
      return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */
      ;
  }
}

static void out_nslog_callback(const transport_log_message *const msg,
                               void *arg) {
  VAR_UNUSED(arg);
  *msg->p = 0;
  CFLog(apple_lvl(msg->lvl), CFSTR("%s"), msg->tag_b);
}

enum { OUT_NSLOG_MASK = TRANSPORT_LOG_PUT_STD & ~TRANSPORT_LOG_PUT_CTX };
#define OUT_NSLOG OUT_NSLOG_MASK, 0, out_nslog_callback
#endif

#if TRANSPORT_LOG_USE_DEBUGSTRING
#include <windows.h>

static void out_debugstring_callback(const transport_log_message *const msg,
                                     void *arg) {
  VAR_UNUSED(arg);
  msg->p[0] = '\n';
  msg->p[1] = '\0';
  OutputDebugStringA(msg->buf);
}

enum { OUT_DEBUGSTRING_MASK = TRANSPORT_LOG_PUT_STD };
#define OUT_DEBUGSTRING OUT_DEBUGSTRING_MASK, 0, out_debugstring_callback
#endif

void transport_log_out_stderr_callback(const transport_log_message *const msg,
                                       void *arg) {
  VAR_UNUSED(arg);
  const size_t eol_len = sizeof(TRANSPORT_LOG_EOL) - 1;
  memcpy(msg->p, TRANSPORT_LOG_EOL, eol_len);
#if defined(_WIN32) || defined(_WIN64)
  /* WriteFile() is atomic for local files opened with FILE_APPEND_DATA and
     without FILE_WRITE_DATA */
  DWORD written;
  WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg->buf,
            (DWORD)(msg->p - msg->buf + eol_len), &written, 0);
#else
  /* write() is atomic for buffers less than or equal to PIPE_BUF. */
  RETVAL_UNUSED(
      write(STDERR_FILENO, msg->buf, (size_t)(msg->p - msg->buf) + eol_len));
#endif
}

static const transport_log_output out_stderr = {TRANSPORT_LOG_OUT_STDERR};

#if !TRANSPORT_LOG_EXTERN_TAG_PREFIX
TRANSPORT_LOG_DEFINE_TAG_PREFIX = 0;
#endif

#if !TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT
TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {TRANSPORT_LOG_MEM_WIDTH};
#endif

#if !TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT
#if TRANSPORT_LOG_USE_ANDROID_LOG
TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_ANDROID};
#elif TRANSPORT_LOG_USE_NSLOG
TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_NSLOG};
#elif TRANSPORT_LOG_USE_DEBUGSTRING
TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_DEBUGSTRING};
#else
TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_OUT_STDERR};
#endif
#endif

#if !TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = 0;
#endif

const transport_log_spec _transport_log_stderr_spec = {
    TRANSPORT_LOG_GLOBAL_FORMAT,
    &out_stderr,
};

static const transport_log_spec global_spec = {
    TRANSPORT_LOG_GLOBAL_FORMAT,
    TRANSPORT_LOG_GLOBAL_OUTPUT,
};

#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(LEVEL, \
                                           TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
static char lvl_char(const int lvl) {
  switch (lvl) {
    case TRANSPORT_LOG_VERBOSE:
      return 'V';
    case TRANSPORT_LOG_DEBUG:
      return 'D';
    case TRANSPORT_LOG_INFO:
      return 'I';
    case TRANSPORT_LOG_WARN:
      return 'W';
    case TRANSPORT_LOG_ERROR:
      return 'E';
    case TRANSPORT_LOG_FATAL:
      return 'F';
    default:
      ASSERT_UNREACHABLE("Bad log level");
      return '?';
  }
}
#endif

#define GCCVER_LESS(MAJOR, MINOR, PATCH)                                  \
  (__GNUC__ < MAJOR || (__GNUC__ == MAJOR && (__GNUC_MINOR__ < MINOR ||   \
                                              (__GNUC_MINOR__ == MINOR && \
                                               __GNUC_PATCHLEVEL__ < PATCH))))

#if !defined(__clang__) && defined(__GNUC__) && GCCVER_LESS(4, 7, 0)
#define __atomic_load_n(vp, model) __sync_fetch_and_add(vp, 0)
#define __atomic_fetch_add(vp, n, model) __sync_fetch_and_add(vp, n)
#define __atomic_sub_fetch(vp, n, model) __sync_sub_and_fetch(vp, n)
#define __atomic_or_fetch(vp, n, model) __sync_or_and_fetch(vp, n)
#define __atomic_and_fetch(vp, n, model) __sync_and_and_fetch(vp, n)
/* Note: will not store old value of *vp in *ep (non-standard behaviour) */
#define __atomic_compare_exchange_n(vp, ep, d, weak, smodel, fmodel) \
  __sync_bool_compare_and_swap(vp, *(ep), d)
#endif

#if !TRANSPORT_LOG_OPTIMIZE_SIZE && !defined(_WIN32) && !defined(_WIN64)
#define TCACHE
#define TCACHE_STALE (0x40000000)
#define TCACHE_FLUID (0x40000000 | 0x80000000)
static unsigned g_tcache_mode = TCACHE_STALE;
static struct timeval g_tcache_tv = {0, 0};
static struct tm g_tcache_tm = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

static INLINE int tcache_get(const struct timeval *const tv,
                             struct tm *const tm) {
  unsigned mode;
  mode = __atomic_load_n(&g_tcache_mode, __ATOMIC_RELAXED);
  if (0 == (mode & TCACHE_FLUID)) {
    mode = __atomic_fetch_add(&g_tcache_mode, 1, __ATOMIC_ACQUIRE);
    if (0 == (mode & TCACHE_FLUID)) {
      if (g_tcache_tv.tv_sec == tv->tv_sec) {
        *tm = g_tcache_tm;
        __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE);
        return !0;
      }
      __atomic_or_fetch(&g_tcache_mode, TCACHE_STALE, __ATOMIC_RELAXED);
    }
    __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE);
  }
  return 0;
}

static INLINE void tcache_set(const struct timeval *const tv,
                              struct tm *const tm) {
  unsigned stale = TCACHE_STALE;
  if (__atomic_compare_exchange_n(&g_tcache_mode, &stale, TCACHE_FLUID, 0,
                                  __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
    g_tcache_tv = *tv;
    g_tcache_tm = *tm;
    __atomic_and_fetch(&g_tcache_mode, ~TCACHE_FLUID, __ATOMIC_RELEASE);
  }
}
#endif

static void time_callback(struct tm *const tm, unsigned *const msec) {
#if !_TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED
  VAR_UNUSED(tm);
  VAR_UNUSED(msec);
#else
#if defined(_WIN32) || defined(_WIN64)
  SYSTEMTIME st;
  GetLocalTime(&st);
  tm->tm_year = st.wYear;
  tm->tm_mon = st.wMonth - 1;
  tm->tm_mday = st.wDay;
  tm->tm_wday = st.wDayOfWeek;
  tm->tm_hour = st.wHour;
  tm->tm_min = st.wMinute;
  tm->tm_sec = st.wSecond;
  *msec = st.wMilliseconds;
#else
  struct timeval tv;
  gettimeofday(&tv, 0);
#ifndef TCACHE
  localtime_r(&tv.tv_sec, tm);
#else
  if (!tcache_get(&tv, tm)) {
    localtime_r(&tv.tv_sec, tm);
    tcache_set(&tv, tm);
  }
#endif
  *msec = (unsigned)tv.tv_usec / 1000;
#endif
#endif
}

static void pid_callback(int *const pid, int *const tid) {
#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(PID, \
                                            TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
  VAR_UNUSED(pid);
#else
#if defined(_WIN32) || defined(_WIN64)
  *pid = GetCurrentProcessId();
#else
  *pid = getpid();
#endif
#endif

#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TID, \
                                            TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
  VAR_UNUSED(tid);
#else
#if defined(_WIN32) || defined(_WIN64)
  *tid = GetCurrentThreadId();
#elif defined(__ANDROID__)
  *tid = gettid();
#elif defined(__linux__)
  *tid = syscall(SYS_gettid);
#elif defined(__MACH__)
  *tid = (int)pthread_mach_thread_np(pthread_self());
#else
#define Platform not supported
#endif
#endif
}

static void buffer_callback(transport_log_message *msg, char *buf) {
  msg->e = (msg->p = msg->buf = buf) + g_buf_sz;
}

#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, \
                                           TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
static const char *funcname(const char *func) { return func ? func : ""; }
#endif

#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, \
                                           TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
static const char *filename(const char *file) {
  const char *f = file;
  for (const char *p = file; 0 != *p; ++p) {
    if ('/' == *p || '\\' == *p) {
      f = p + 1;
    }
  }
  return f;
}
#endif

static INLINE size_t nprintf_size(transport_log_message *const msg) {
  // *nprintf() always puts 0 in the end when input buffer is not empty. This
  // 0 is not desired because its presence sets (ctx->p) to (ctx->e - 1) which
  // leaves space for one more character. Some put_xxx() functions don't use
  // *nprintf() and could use that last character. In that case log line will
  // have multiple (two) half-written parts which is confusing. To workaround
  // that we allow *nprintf() to write its 0 in the eol area (which is always
  // not empty).
  return (size_t)(msg->e - msg->p + 1);
}

static INLINE void put_nprintf(transport_log_message *const msg, const int n) {
  if (0 < n) {
    msg->p = n < msg->e - msg->p ? msg->p + n : msg->e;
  }
}

static INLINE char *put_padding_r(const unsigned w, const char wc, char *p,
                                  char *e) {
  for (char *const b = e - w; b < p; *--p = wc) {
  }
  return p;
}

static char *put_integer_r(unsigned v, const int sign, const unsigned w,
                           const char wc, char *const e) {
  static const char _signs[] = {'-', '0', '+'};
  static const char *const signs = _signs + 1;
  char *p = e;
  do {
    *--p = '0' + v % 10;
  } while (0 != (v /= 10));
  if (0 == sign) return put_padding_r(w, wc, p, e);
  if ('0' != wc) {
    *--p = signs[sign];
    return put_padding_r(w, wc, p, e);
  }
  p = put_padding_r(w, wc, p, e + 1);
  *--p = signs[sign];
  return p;
}

static INLINE char *put_uint_r(const unsigned v, const unsigned w,
                               const char wc, char *const e) {
  return put_integer_r(v, 0, w, wc, e);
}

static INLINE char *put_int_r(const int v, const unsigned w, const char wc,
                              char *const e) {
  return 0 <= v ? put_integer_r((unsigned)v, 0, w, wc, e)
                : put_integer_r((unsigned)-v, -1, w, wc, e);
}

static INLINE char *put_stringn(const char *const s_p, const char *const s_e,
                                char *const p, char *const e) {
  const ptrdiff_t m = e - p;
  ptrdiff_t n = s_e - s_p;
  if (n > m) {
    n = m;
  }
  memcpy(p, s_p, n);
  return p + n;
}

static INLINE char *put_string(const char *s, char *p, char *const e) {
  const ptrdiff_t n = e - p;
  char *const c = (char *)memccpy(p, s, '\0', n);
  return 0 != c ? c - 1 : e;
}

static INLINE char *put_uint(unsigned v, const unsigned w, const char wc,
                             char *const p, char *const e) {
  char buf[16];
  char *const se = buf + _countof(buf);
  char *sp = put_uint_r(v, w, wc, se);
  return put_stringn(sp, se, p, e);
}

#define PUT_CSTR_R(p, STR)                         \
  do {                                             \
    for (unsigned i = sizeof(STR) - 1; 0 < i--;) { \
      *--(p) = (STR)[i];                           \
    }                                              \
  }                                                \
  _TRANSPORT_LOG_ONCE

#define PUT_CSTR_CHECKED(p, e, STR)                                 \
  do {                                                              \
    for (unsigned i = 0; (e) > (p) && (sizeof(STR) - 1) > i; ++i) { \
      *(p)++ = (STR)[i];                                            \
    }                                                               \
  }                                                                 \
  _TRANSPORT_LOG_ONCE

/* F_INIT field support.
 */
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__YEAR
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MONTH
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__DAY
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__HOUR
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MINUTE
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__SECOND
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MILLISECOND
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__PID
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__TID
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__LEVEL
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__TAG(ps, ts)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FUNCTION
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FILENAME
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FILELINE
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__S(s)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__F_INIT(expr) _PP_UNTUPLE(expr);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__F_UINT(w, v)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT(field) \
  _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT_, _, field)

/* Implements generation of printf-like format string for log message
 * format specification.
 */
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__ ""
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__YEAR "%04u"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MONTH "%02u"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__DAY "%02u"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__HOUR "%02u"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MINUTE "%02u"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__SECOND "%02u"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MILLISECOND "%03u"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__PID "%5i"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TID "%5i"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__LEVEL "%c"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TAG UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FUNCTION "%s"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILENAME "%s"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILELINE "%u"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__S(s) s
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_INIT(expr) ""
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_UINT(w, v) "%" #w "u"
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT(field) \
  _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT_, _, field)

/* Implements generation of printf-like format parameters for log message
 * format specification.
 */
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__YEAR \
  , (unsigned)(tm.tm_year + 1900)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MONTH \
  , (unsigned)(tm.tm_mon + 1)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__DAY , (unsigned)tm.tm_mday
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__HOUR , (unsigned)tm.tm_hour
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MINUTE , (unsigned)tm.tm_min
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__SECOND , (unsigned)tm.tm_sec
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MILLISECOND , (unsigned)msec
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__PID , pid
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TID , tid
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__LEVEL \
  , (char)lvl_char(msg->lvl)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TAG UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FUNCTION , funcname(src->func)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILENAME , filename(src->file)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILELINE , src->line
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__S(s)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_INIT(expr)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_UINT(w, v) , v
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL(field) \
  _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL_, _, field)

/* Implements generation of put_xxx_t statements for log message specification.
 */
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__YEAR \
  p = put_uint_r(tm.tm_year + 1900, 4, '0', p);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MONTH \
  p = put_uint_r((unsigned)tm.tm_mon + 1, 2, '0', p);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__DAY \
  p = put_uint_r((unsigned)tm.tm_mday, 2, '0', p);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__HOUR \
  p = put_uint_r((unsigned)tm.tm_hour, 2, '0', p);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MINUTE \
  p = put_uint_r((unsigned)tm.tm_min, 2, '0', p);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__SECOND \
  p = put_uint_r((unsigned)tm.tm_sec, 2, '0', p);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MILLISECOND \
  p = put_uint_r(msec, 3, '0', p);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__PID p = put_int_r(pid, 5, ' ', p);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__TID p = put_int_r(tid, 5, ' ', p);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__LEVEL *--p = lvl_char(msg->lvl);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__TAG UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FUNCTION UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FILENAME UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FILELINE UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__S(s) PUT_CSTR_R(p, s);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__F_INIT(expr)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__F_UINT(w, v) \
  p = put_uint_r(v, w, ' ', p);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R(field) \
  _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R_, _, field)

static void put_ctx(transport_log_message *const msg) {
  _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
  VAR_UNUSED(msg);
#else
#if _TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED
  struct tm tm;
  unsigned msec;
  g_time_cb(&tm, &msec);
#endif
#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(     \
    PID, TRANSPORT_LOG_MESSAGE_CTX_FORMAT) ||   \
    _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TID, \
                                           TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
  int pid, tid;
  g_pid_cb(&pid, &tid);
#endif

#if TRANSPORT_LOG_OPTIMIZE_SIZE
  int n;
  n = snprintf(msg->p, nprintf_size(msg),
               _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT,
                       TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
                   _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL,
                           TRANSPORT_LOG_MESSAGE_CTX_FORMAT));
  put_nprintf(msg, n);
#else
  char buf[64];
  char *const e = buf + sizeof(buf);
  char *p = e;
  _PP_RMAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R,
           TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
  msg->p = put_stringn(p, e, msg->p, msg->e);
#endif
#endif
}

#define PUT_TAG(msg, tag, prefix_delim, tag_delim)                       \
  do {                                                                   \
    const char *ch;                                                      \
    msg->tag_b = msg->p;                                                 \
    if (0 != (ch = _transport_log_tag_prefix)) {                         \
      for (; msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) { \
      }                                                                  \
    }                                                                    \
    if (0 != (ch = tag) && 0 != tag[0]) {                                \
      if (msg->tag_b != msg->p) {                                        \
        PUT_CSTR_CHECKED(msg->p, msg->e, prefix_delim);                  \
      }                                                                  \
      for (; msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) { \
      }                                                                  \
    }                                                                    \
    msg->tag_e = msg->p;                                                 \
    if (msg->tag_b != msg->p) {                                          \
      PUT_CSTR_CHECKED(msg->p, msg->e, tag_delim);                       \
    }                                                                    \
  }                                                                      \
  _TRANSPORT_LOG_ONCE

/* Implements simple put statements for log message specification.
 */
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__YEAR UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MONTH UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__DAY UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__HOUR UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MINUTE UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__SECOND UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MILLISECOND UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__PID UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__TID UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__LEVEL UNDEFINED
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__TAG(pd, td) \
  PUT_TAG(msg, tag, pd, td);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FUNCTION \
  msg->p = put_string(funcname(src->func), msg->p, msg->e);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FILENAME \
  msg->p = put_string(filename(src->file), msg->p, msg->e);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FILELINE \
  msg->p = put_uint(src->line, 0, '\0', msg->p, msg->e);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__S(s) \
  PUT_CSTR_CHECKED(msg->p, msg->e, s);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__F_INIT(expr)
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__F_UINT(w, v) \
  msg->p = put_uint(v, w, ' ', msg->p, msg->e);
#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT(field) \
  _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_, _, field)

static void put_tag(transport_log_message *const msg, const char *const tag) {
  _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_TAG_FORMAT)
#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, \
                                            TRANSPORT_LOG_MESSAGE_TAG_FORMAT)
  VAR_UNUSED(tag);
#endif
#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_TAG_FORMAT)
  VAR_UNUSED(msg);
#else
  _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT, TRANSPORT_LOG_MESSAGE_TAG_FORMAT)
#endif
}

static void put_src(transport_log_message *const msg,
                    const src_location *const src) {
  _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(           \
    FUNCTION, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) &&     \
    !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(           \
        FILENAME, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) && \
    !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FILELINE,  \
                                            TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
  VAR_UNUSED(src);
#endif
#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
  VAR_UNUSED(msg);
#else
#if TRANSPORT_LOG_OPTIMIZE_SIZE
  int n;
  n = snprintf(msg->p, nprintf_size(msg),
               _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT,
                       TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
                   _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL,
                           TRANSPORT_LOG_MESSAGE_SRC_FORMAT));
  put_nprintf(msg, n);
#else
  _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT, TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
#endif
#endif
}

static void put_msg(transport_log_message *const msg, const char *const fmt,
                    va_list va) {
  int n;
  msg->msg_b = msg->p;
  n = vsnprintf(msg->p, nprintf_size(msg), fmt, va);
  put_nprintf(msg, n);
}

static void output_mem(const transport_log_spec *log,
                       transport_log_message *const msg,
                       const mem_block *const mem) {
  if (0 == mem->d || 0 == mem->d_sz) {
    return;
  }
  const unsigned char *mem_p = (const unsigned char *)mem->d;
  const unsigned char *const mem_e = mem_p + mem->d_sz;
  const unsigned char *mem_cut;
  const ptrdiff_t mem_width = (ptrdiff_t)log->format->mem_width;
  char *const hex_b = msg->msg_b;
  char *const ascii_b = hex_b + 2 * mem_width + 2;
  char *const ascii_e = ascii_b + mem_width;
  if (msg->e < ascii_e) {
    return;
  }
  while (mem_p != mem_e) {
    char *hex = hex_b;
    char *ascii = ascii_b;
    for (mem_cut = mem_width < mem_e - mem_p ? mem_p + mem_width : mem_e;
         mem_cut != mem_p; ++mem_p) {
      const unsigned char ch = *mem_p;
      *hex++ = c_hex[(0xf0 & ch) >> 4];
      *hex++ = c_hex[(0x0f & ch)];
      *ascii++ = isprint(ch) ? (char)ch : '?';
    }
    while (hex != ascii_b) {
      *hex++ = ' ';
    }
    msg->p = ascii;
    log->output->callback(msg, log->output->arg);
  }
}

void transport_log_set_tag_prefix(const char *const prefix) {
  _transport_log_tag_prefix = prefix;
}

void transport_log_set_mem_width(const unsigned w) {
  _transport_log_global_format.mem_width = w;
}

void transport_log_set_output_level(const int lvl) {
  _transport_log_global_output_lvl = lvl;
}

void transport_log_set_output_v(const unsigned mask, void *const arg,
                                const transport_log_output_cb callback) {
  _transport_log_global_output.mask = mask;
  _transport_log_global_output.arg = arg;
  _transport_log_global_output.callback = callback;
}

static void _transport_log_write_imp(const transport_log_spec *log,
                                     const src_location *const src,
                                     const mem_block *const mem, const int lvl,
                                     const char *const tag,
                                     const char *const fmt, va_list va) {
  transport_log_message msg;
  char buf[TRANSPORT_LOG_BUF_SZ];
  const unsigned mask = log->output->mask;
  msg.lvl = lvl;
  msg.tag = tag;
  g_buffer_cb(&msg, buf);
  if (TRANSPORT_LOG_PUT_CTX & mask) {
    put_ctx(&msg);
  }
  if (TRANSPORT_LOG_PUT_TAG & mask) {
    put_tag(&msg, tag);
  }
  if (0 != src && TRANSPORT_LOG_PUT_SRC & mask) {
    put_src(&msg, src);
  }
  if (TRANSPORT_LOG_PUT_MSG & mask) {
    put_msg(&msg, fmt, va);
  }
  log->output->callback(&msg, log->output->arg);
  if (0 != mem && TRANSPORT_LOG_PUT_MSG & mask) {
    output_mem(log, &msg, mem);
  }
}

void _transport_log_write_d(const char *const func, const char *const file,
                            const unsigned line, const int lvl,
                            const char *const tag, const char *const fmt, ...) {
  const src_location src = {func, file, line};
  va_list va;
  va_start(va, fmt);
  _transport_log_write_imp(&global_spec, &src, 0, lvl, tag, fmt, va);
  va_end(va);
}

void _transport_log_write_aux_d(const char *const func, const char *const file,
                                const unsigned line,
                                const transport_log_spec *const log,
                                const int lvl, const char *const tag,
                                const char *const fmt, ...) {
  const src_location src = {func, file, line};
  va_list va;
  va_start(va, fmt);
  _transport_log_write_imp(log, &src, 0, lvl, tag, fmt, va);
  va_end(va);
}

void _transport_log_write(const int lvl, const char *const tag,
                          const char *const fmt, ...) {
  va_list va;
  va_start(va, fmt);
  _transport_log_write_imp(&global_spec, 0, 0, lvl, tag, fmt, va);
  va_end(va);
}

void _transport_log_write_aux(const transport_log_spec *const log,
                              const int lvl, const char *const tag,
                              const char *const fmt, ...) {
  va_list va;
  va_start(va, fmt);
  _transport_log_write_imp(log, 0, 0, lvl, tag, fmt, va);
  va_end(va);
}

void _transport_log_write_mem_d(const char *const func, const char *const file,
                                const unsigned line, const int lvl,
                                const char *const tag, const void *const d,
                                const unsigned d_sz, const char *const fmt,
                                ...) {
  const src_location src = {func, file, line};
  const mem_block mem = {d, d_sz};
  va_list va;
  va_start(va, fmt);
  _transport_log_write_imp(&global_spec, &src, &mem, lvl, tag, fmt, va);
  va_end(va);
}

void _transport_log_write_mem_aux_d(const char *const func,
                                    const char *const file, const unsigned line,
                                    const transport_log_spec *const log,
                                    const int lvl, const char *const tag,
                                    const void *const d, const unsigned d_sz,
                                    const char *const fmt, ...) {
  const src_location src = {func, file, line};
  const mem_block mem = {d, d_sz};
  va_list va;
  va_start(va, fmt);
  _transport_log_write_imp(log, &src, &mem, lvl, tag, fmt, va);
  va_end(va);
}

void _transport_log_write_mem(const int lvl, const char *const tag,
                              const void *const d, const unsigned d_sz,
                              const char *const fmt, ...) {
  const mem_block mem = {d, d_sz};
  va_list va;
  va_start(va, fmt);
  _transport_log_write_imp(&global_spec, 0, &mem, lvl, tag, fmt, va);
  va_end(va);
}

void _transport_log_write_mem_aux(const transport_log_spec *const log,
                                  const int lvl, const char *const tag,
                                  const void *const d, const unsigned d_sz,
                                  const char *const fmt, ...) {
  const mem_block mem = {d, d_sz};
  va_list va;
  va_start(va, fmt);
  _transport_log_write_imp(log, 0, &mem, lvl, tag, fmt, va);
  va_end(va);
}