denizzzka / dpq2
1
/*
2
*   PostgreSQL numeric format
3
*
4
*   Copyright: © 2014 DSoftOut
5
*   Authors: NCrashed <ncrashed@gmail.com>
6
*/
7
module dpq2.conv.numeric;
8

9
private pure // inner representation from libpq sources
10
{
11
    alias NumericDigit = ushort;
12
    enum DEC_DIGITS = 4;
13
    enum NUMERIC_NEG       = 0x4000;
14
    enum NUMERIC_NAN       = 0xC000;
15

16
    struct NumericVar
17
    {
18
        int weight;
19
        int sign;
20
        int dscale;
21
        NumericDigit[] digits;
22
    }
23

24
    string numeric_out(in NumericVar num)
25
    {
26 0
        string str;
27

28 0
        if(num.sign == NUMERIC_NAN)
29
        {
30 0
	    return "NaN";
31
        }
32

33 0
        str = get_str_from_var(num);
34

35 0
        return str;
36
    }
37

38
    /*
39
     * get_str_from_var() -
40
     *
41
     *  Convert a var to text representation (guts of numeric_out).
42
     *  The var is displayed to the number of digits indicated by its dscale.
43
     *  Returns a palloc'd string.
44
     */
45
    string get_str_from_var(in NumericVar var)
46
    {
47 0
	int          dscale;
48 0
	ubyte[]      str;
49 0
	ubyte*       cp;
50 0
	ubyte*       endcp;
51 0
	int          i;
52 0
	int          d;
53 0
	NumericDigit dig;
54

55
	static if(DEC_DIGITS > 1)
56
	{
57 0
	    NumericDigit d1;
58
	}
59

60 0
	dscale = var.dscale;
61

62
	/*
63
	 * Allocate space for the result.
64
	 *
65
	 * i is set to the # of decimal digits before decimal point. dscale is the
66
	 * # of decimal digits we will print after decimal point. We may generate
67
	 * as many as DEC_DIGITS-1 excess digits at the end, and in addition we
68
	 * need room for sign, decimal point, null terminator.
69
	 */
70 0
	i = (var.weight + 1) * DEC_DIGITS;
71 0
	if (i <= 0)
72 0
	    i = 1;
73

74 0
	str = new ubyte[i + dscale + DEC_DIGITS + 2];
75 0
	cp = str.ptr;
76

77
	/*
78
	 * Output a dash for negative values
79
	 */
80 0
	if (var.sign == NUMERIC_NEG)
81 0
	    *cp++ = '-';
82

83
	/*
84
	 * Output all digits before the decimal point
85
	 */
86 0
	if (var.weight < 0)
87
	{
88 0
	    d = var.weight + 1;
89 0
	    *cp++ = '0';
90
	}
91
	else
92
	{
93 0
	    for (d = 0; d <= var.weight; d++)
94
	    {
95 0
		dig = (d < var.digits.length) ? var.digits[d] : 0;
96
		/* In the first digit, suppress extra leading decimal zeroes */
97
		static if(DEC_DIGITS == 4)
98
		{
99 0
		    bool putit = (d > 0);
100

101 0
		    d1 = dig / 1000;
102 0
		    dig -= d1 * 1000;
103 0
		    putit |= (d1 > 0);
104 0
		    if (putit)
105 0
			*cp++ = cast(char)(d1 + '0');
106 0
		    d1 = dig / 100;
107 0
		    dig -= d1 * 100;
108 0
		    putit |= (d1 > 0);
109 0
		    if (putit)
110 0
			*cp++ = cast(char)(d1 + '0');
111 0
		    d1 = dig / 10;
112 0
		    dig -= d1 * 10;
113 0
		    putit |= (d1 > 0);
114 0
		    if (putit)
115 0
			*cp++ = cast(char)(d1 + '0');
116 0
		    *cp++ = cast(char)(dig + '0');
117
		}
118
		else static if(DEC_DIGITS == 2)
119
		{
120
			d1 = dig / 10;
121
			dig -= d1 * 10;
122
			if (d1 > 0 || d > 0)
123
			    *cp++ = cast(char)(d1 + '0');
124
			*cp++ = cast(char)(dig + '0');
125
		}
126
		else static if(DEC_DIGITS == 1)
127
		{
128
		    *cp++ = cast(char)(dig + '0');
129
		}
130
		else pragma(error, "unsupported NBASE");
131
	    }
132
	}
133

134
	/*
135
	 * If requested, output a decimal point and all the digits that follow it.
136
	 * We initially put out a multiple of DEC_DIGITS digits, then truncate if
137
	 * needed.
138
	 */
139 0
	if (dscale > 0)
140
	{
141 0
	    *cp++ = '.';
142 0
	    endcp = cp + dscale;
143 0
	    for (i = 0; i < dscale; d++, i += DEC_DIGITS)
144
	    {
145 0
		dig = (d >= 0 && d < var.digits.length) ? var.digits[d] : 0;
146
		static if(DEC_DIGITS == 4)
147
		{
148 0
			d1 = dig / 1000;
149 0
			dig -= d1 * 1000;
150 0
			*cp++ = cast(char)(d1 + '0');
151 0
			d1 = dig / 100;
152 0
			dig -= d1 * 100;
153 0
			*cp++ = cast(char)(d1 + '0');
154 0
			d1 = dig / 10;
155 0
			dig -= d1 * 10;
156 0
			*cp++ = cast(char)(d1 + '0');
157 0
			*cp++ = cast(char)(dig + '0');
158
		}
159
		else static if(DEC_DIGITS == 2)
160
		{
161
			d1 = dig / 10;
162
			dig -= d1 * 10;
163
			*cp++ = cast(char)(d1 + '0');
164
			*cp++ = cast(char)(dig + '0');
165
		}
166
		else static if(DEC_DIGITS == 1)
167
		{
168
		    *cp++ = cast(char)(dig + '0');
169
	    }
170
	    else pragma(error, "unsupported NBASE");
171
	    }
172 0
	    cp = endcp;
173
	}
174

175
	/*
176
	 * terminate the string and return it
177
	 */
178 0
	*cp = '\0';
179

180 0
	return (cast(char*) str).fromStringz;
181
    }
182
}
183

184
import std.conv: to;
185
import std.string: fromStringz;
186
import std.bitmanip: bigEndianToNative;
187

188
package string rawValueToNumeric(in ubyte[] v) pure
189
{
190
    import dpq2.result: ValueConvException, ConvExceptionType;
191

192
    struct NumericVar_net // network byte order
193
    {
194
	ubyte[2] num; // num of digits
195
        ubyte[2] weight;
196
        ubyte[2] sign;
197
        ubyte[2] dscale;
198
    }
199

200 0
    if(!(v.length >= NumericVar_net.sizeof))
201 0
        throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH,
202
            "Value length ("~to!string(v.length)~") less than it is possible for numeric type",
203
            __FILE__, __LINE__);
204

205 0
    NumericVar_net* h = cast(NumericVar_net*) v.ptr;
206

207 0
    NumericVar res;
208 0
    res.weight = bigEndianToNative!short(h.weight);
209 0
    res.sign   = bigEndianToNative!ushort(h.sign);
210 0
    res.dscale = bigEndianToNative!ushort(h.dscale);
211

212 0
    auto len = (v.length - NumericVar_net.sizeof) / NumericDigit.sizeof;
213

214 0
    res.digits = new NumericDigit[len];
215

216 0
    size_t offset = NumericVar_net.sizeof;
217 0
    foreach(i; 0 .. len)
218
    {
219 0
	res.digits[i] = bigEndianToNative!NumericDigit(
220
		(&(v[offset]))[0..NumericDigit.sizeof]
221
	    );
222 0
	offset += NumericDigit.sizeof;
223
    }
224

225 0
    return numeric_out(res);
226
}

Read our documentation on viewing source code .

Loading