1 /*
2  * Copyright (C) 2019, HuntLabs
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 module hunt.database.base.Util;
19 
20 import hunt.Byte;
21 import hunt.net.buffer.ByteBuf;
22 import hunt.net.buffer.ByteBufUtil;
23 import hunt.text.Charset;
24 import hunt.util.TypeUtils;
25 
26 import std.algorithm;
27 import std.conv;
28 import std.variant;
29 
30 import core.stdc.stdio;
31 import core.stdc.stdarg;
32 import core.stdc.time;
33 
34 import hunt.logging;
35 
36 
37 /**
38  * 
39  */
40 class Util {
41 
42     private enum byte ZERO = 0;
43 
44     static string readCString(ByteBuf src, Charset charset) {
45         string data;
46         int len = src.bytesBefore(ZERO);
47 
48         version(HUNT_DB_DEBUG_MORE) {
49             tracef("len: %d, buffer: %s", len, src);
50         }
51 
52         string s = src.readCharSequence(len, charset);
53         src.readByte();
54         return s;
55     }
56 
57     static string readCStringUTF8(ByteBuf src) {
58         return readCString(src, StandardCharsets.UTF_8);
59     }
60 
61     static void writeCString(ByteBuf dst, string s, Charset charset) {
62         dst.writeCharSequence(s, charset);
63         dst.writeByte(0);
64     }
65 
66     static void writeCString(ByteBuf dst, ByteBuf buf) {
67         // Important : won't not change data index
68         dst.writeBytes(buf, buf.readerIndex(), buf.readableBytes());
69         dst.writeByte(0);
70     }
71 
72     static void writeCStringUTF8(ByteBuf dst, string s) {
73         dst.writeCharSequence(s, StandardCharsets.UTF_8);
74         dst.writeByte(0);
75     }
76 
77     static void writeCString(ByteBuf dst, byte[] bytes) {
78         dst.writeBytes(bytes, 0, cast(int)bytes.length);
79         dst.writeByte(0);
80     }
81 
82     static string buildInvalidArgsError(Variant[] values, string[] types) {
83         import std.format;
84         // string str = types.to!string();
85         string str = format("[%-(%s / %)]", types);
86         return "Values [" ~ values.map!(v => v.to!string() ~ " : "~ v.type.toString() ).joiner(", ").to!string() ~
87             "] cannot be coerced to " ~ str;
88     }
89 
90     static string buildInvalidArgsError(T)(T[] values, TypeInfo[] types) {
91         return "Values [" ~ values.map!(v => v.to!string()).joiner(", ").to!string() ~
92             "] cannot be coerced to [" ~ types.map!(t => TypeUtils.getSimpleName(t))
93             .joiner(", ").to!string() ~ "]";
94     }
95 
96     // private enum int FIRST_HALF_BYTE_MASK = 0x0F;
97 
98     // static int writeHexString(Buffer buffer, ByteBuf to) {
99     //     int len = buffer.length();
100     //     for (int i = 0; i < len; i++) {
101     //         int b = Byte.toUnsignedInt(buffer.getByte(i));
102     //         int firstDigit = b >> 4;
103     //         byte firstHexDigit = cast(byte)bin2hex(firstDigit);
104     //         int secondDigit = b & FIRST_HALF_BYTE_MASK;
105     //         byte secondHexDigit = cast(byte)bin2hex(secondDigit);
106     //         to.writeByte(firstHexDigit);
107     //         to.writeByte(secondHexDigit);
108     //     }
109     //     return len;
110     // }
111 
112     // private static int bin2hex(int digit){
113     //     int isLessOrEqual9 =(digit-10)>>31;
114     //     //isLessOrEqual9==0xff<->digit<=9
115     //     //bin2hexAsciiDistance=digit<=9?48:87;
116     //     int bin2hexAsciiDistance = 48+((~isLessOrEqual9)&39);
117     //     return digit+bin2hexAsciiDistance;
118     // }
119 
120 
121     extern(C) @nogc nothrow {
122         static void info(string file = __FILE__, size_t line = __LINE__,
123                     string func = __FUNCTION__)(cstring msg) {
124             version(Windows) {
125                 doLog!("info", file, line, func)( msg);
126             } else { 
127                 doLog!("info", CONSOLE_COLOR_BLUE, file, line, func)(msg);
128             }
129         }
130 
131         static void infof(string file = __FILE__, size_t line = __LINE__,
132                     string func = __FUNCTION__)(cstring fmt, ...) {
133             va_list args;
134             va_start(args, fmt);
135             char[512] buffer;
136             vsnprintf(buffer.ptr, buffer.length, fmt, args);
137             version(Windows) {
138                 doLog!("info", file, line, func)(buffer.ptr);
139             } else {
140                 doLog!("info", CONSOLE_COLOR_BLUE, file, line, func)(buffer.ptr);
141             }
142             va_end(args);
143         }
144 
145         version(Windows) {
146             static private void doLog(string level, string file = __FILE__, size_t line = __LINE__,
147                         string func = __FUNCTION__)(cstring msg) {
148                 time_t     now;
149                 tm  ts;
150                 char[24]       buf;
151 
152                 // Get current time
153                 time(&now);
154 
155                 ts = *localtime(&now);
156                 strftime(buf.ptr, buf.length, "%m-%d %H:%M:%S", &ts);
157 
158                 printf("%s.%d | %s | %d | %s | %s | %s:%d\n", buf.ptr, 0, level.ptr, 
159                     getTid(), cast(cstring)func.ptr, msg, cast(cstring)file.ptr, line);
160             }  
161 
162         } else {
163 
164             static private void doLog(string level, string leadingColor, string file = __FILE__, size_t line = __LINE__,
165                         string func = __FUNCTION__)(cstring msg) {
166                 time_t     now;
167                 tm  ts;
168                 char[24]       buf;
169 
170                 // Get current time
171                 time(&now);
172 
173                 ts = *localtime(&now);
174                 strftime(buf.ptr, buf.length, "%m-%d %H:%M:%S", &ts);
175 
176                 version(X86_64) {
177                     printf("%s%s.%d | %s | %llu | %s | %s | %s:%llu%s\n", leadingColor.ptr, buf.ptr, 0, level.ptr, 
178                         getTid(), cast(cstring)func.ptr, msg, 
179                         cast(cstring)file.ptr, line, CONSOLE_COLOR_NONE.ptr);
180                 } else {
181                     printf("%s%s.%d | %s | %u | %s | %s | %s:%u%s\n", leadingColor.ptr, buf.ptr, 0, level.ptr, 
182                         getTid(), cast(cstring)func.ptr, msg, 
183                         cast(cstring)file.ptr, line, CONSOLE_COLOR_NONE.ptr);
184                 }
185                             
186             }
187 
188         }
189     }
190 }
191 
192 private alias cstring = const(char)*;
193 
194 extern(C) @nogc nothrow : // 
195 
196 
197 version (Posix) {
198     import core.sys.posix.sys.types : pthread_t; 
199     size_t syscall(size_t ident, ...);
200 
201     ThreadID getTid() {
202         version(FreeBSD) {
203             long tid;
204             enum SYS_thr_self = 432;
205             syscall(SYS_thr_self, &tid);
206             return cast(ThreadID)tid;
207         } else version(OSX) {
208             enum SYS_thread_selfid = 372;
209             return cast(ThreadID)syscall(SYS_thread_selfid);
210         } else version(linux) {
211             enum __NR_gettid = 186;
212             return cast(ThreadID)syscall(__NR_gettid);
213         } else {
214             return 0;
215         }
216     }
217     
218     // https://misc.flogisoft.com/bash/tip_colors_and_formatting
219     // https://solarianprogrammer.com/2019/04/08/c-programming-ansi-escape-codes-windows-macos-linux-terminals/
220     // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences?redirectedfrom=MSDN
221     enum CONSOLE_COLOR_NONE = "\033[m";
222     enum CONSOLE_COLOR_RED = "\033[0;32;31m";
223     enum CONSOLE_COLOR_GREEN = "\033[0;32;32m";
224     enum CONSOLE_COLOR_YELLOW = "\033[1;33m";
225     enum CONSOLE_COLOR_BLUE = "\033[34m";
226 
227     alias ThreadID = pthread_t;
228 
229 } else {
230     
231     import core.sys.windows.wincon;
232     import core.sys.windows.winbase;
233     import core.sys.windows.windef;
234 
235     alias ThreadID = uint;
236 
237     ThreadID getTid() {
238         return GetCurrentThreadId();
239     }
240 }