1 /* abcio.cpp
2  *
3  * Copyright (C) 1992-2011,2015,2017-2020 Paul Boersma
4  *
5  * This code is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  *
10  * This code is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this work. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "melder.h"
20 #ifdef macintosh
21 	#include <TargetConditionals.h>
22 #endif
23 
24 /********** text I/O **********/
25 
getInteger(MelderReadText me)26 static int64 getInteger (MelderReadText me) {
27 	char buffer [41];
28 	char32 c;
29 	/*
30 	 * Look for the first numeric character.
31 	 */
32 	for (c = MelderReadText_getChar (me); c != U'-' && ! Melder_isAsciiDecimalNumber (c) && c != U'+'; c = MelderReadText_getChar (me)) {
33 		if (c == U'\0')
34 			Melder_throw (U"Early end of text detected while looking for an integer (line ", MelderReadText_getLineNumber (me), U").");
35 		if (c == U'!') {   // end-of-line comment?
36 			while ((c = MelderReadText_getChar (me)) != U'\n' && c != U'\r') {
37 				if (c == 0)
38 					Melder_throw (U"Early end of text detected in comment while looking for an integer (line ", MelderReadText_getLineNumber (me), U").");
39 			}
40 		}
41 		if (c == U'\"')
42 			Melder_throw (U"Found a string while looking for an integer in text (line ", MelderReadText_getLineNumber (me), U").");
43 		if (c == U'<')
44 			Melder_throw (U"Found an enumerated value while looking for an integer in text (line ", MelderReadText_getLineNumber (me), U").");
45 		while (! Melder_isHorizontalOrVerticalSpace (c)) {
46 			if (c == U'\0')
47 				Melder_throw (U"Early end of text detected in comment (line ", MelderReadText_getLineNumber (me), U").");
48 			c = MelderReadText_getChar (me);
49 		}
50 	}
51 	int i = 0;
52 	for (; i < 40; i ++) {
53 		if (c > 127)
54 			Melder_throw (U"Found strange text while looking for an integer in text (line ", MelderReadText_getLineNumber (me), U").");
55 		buffer [i] = (char) (char8) c;   // guarded conversion down
56 		c = MelderReadText_getChar (me);
57 		if (c == U'\0') { break; }   // this may well be OK here
58 		if (Melder_isHorizontalOrVerticalSpace (c)) break;
59 	}
60 	if (i >= 40)
61 		Melder_throw (U"Found long text while looking for an integer in text (line ", MelderReadText_getLineNumber (me), U").");
62 	buffer [i + 1] = '\0';
63 	return strtoll (buffer, nullptr, 10);
64 }
65 
getUnsigned(MelderReadText me)66 static uint64 getUnsigned (MelderReadText me) {
67 	char buffer [41];
68 	char32 c;
69 	for (c = MelderReadText_getChar (me); ! Melder_isAsciiDecimalNumber (c) && c != U'+'; c = MelderReadText_getChar (me)) {
70 		if (c == U'\0')
71 			Melder_throw (U"Early end of text detected while looking for an unsigned integer (line ", MelderReadText_getLineNumber (me), U").");
72 		if (c == U'!') {   // end-of-line comment?
73 			while ((c = MelderReadText_getChar (me)) != '\n' && c != '\r') {
74 				if (c == U'\0')
75 					Melder_throw (U"Early end of text detected in comment while looking for an unsigned integer (line ", MelderReadText_getLineNumber (me), U").");
76 			}
77 		}
78 		if (c == U'\"')
79 			Melder_throw (U"Found a string while looking for an unsigned integer in text (line ", MelderReadText_getLineNumber (me), U").");
80 		if (c == U'<')
81 			Melder_throw (U"Found an enumerated value while looking for an unsigned integer in text (line ", MelderReadText_getLineNumber (me), U").");
82 		if (c == U'-')
83 			Melder_throw (U"Found a negative value while looking for an unsigned integer in text (line ", MelderReadText_getLineNumber (me), U").");
84 		while (! Melder_isHorizontalOrVerticalSpace (c)) {
85 			if (c == U'\0')
86 				Melder_throw (U"Early end of text detected in comment (line ", MelderReadText_getLineNumber (me), U").");
87 			c = MelderReadText_getChar (me);
88 		}
89 	}
90 	int i = 0;
91 	for (i = 0; i < 40; i ++) {
92 		if (c > 127)
93 			Melder_throw (U"Found strange text while looking for an unsigned integer in text (line ", MelderReadText_getLineNumber (me), U").");
94 		buffer [i] = (char) (char8) c;   // guarded conversion down
95 		c = MelderReadText_getChar (me);
96 		if (c == U'\0') { break; }   // this may well be OK here
97 		if (Melder_isHorizontalOrVerticalSpace (c)) break;
98 	}
99 	if (i >= 40)
100 		Melder_throw (U"Found long text while searching for an unsigned integer in text (line ", MelderReadText_getLineNumber (me), U").");
101 	buffer [i + 1] = '\0';
102 	return strtoull (buffer, nullptr, 10);
103 }
104 
getReal(MelderReadText me)105 static double getReal (MelderReadText me) {
106 	int i;
107 	char buffer [41], *slash;
108 	char32 c;
109 	do {
110 		for (c = MelderReadText_getChar (me); c != U'-' && ! Melder_isAsciiDecimalNumber (c) && c != U'+'; c = MelderReadText_getChar (me)) {
111 			if (c == U'\0')
112 				Melder_throw (U"Early end of text detected while looking for a real number (line ", MelderReadText_getLineNumber (me), U").");
113 			if (c == U'!') {   // end-of-line comment?
114 				while ((c = MelderReadText_getChar (me)) != U'\n' && c != U'\r') {
115 					if (c == U'\0')
116 						Melder_throw (U"Early end of text detected in comment while looking for a real number (line ", MelderReadText_getLineNumber (me), U").");
117 				}
118 			}
119 			if (c == U'\"')
120 				Melder_throw (U"Found a string while looking for a real number in text (line ", MelderReadText_getLineNumber (me), U").");
121 			if (c == U'<')
122 				Melder_throw (U"Found an enumerated value while looking for a real number in text (line ", MelderReadText_getLineNumber (me), U").");
123 			while (! Melder_isHorizontalOrVerticalSpace (c)) {
124 				if (c == U'\0')
125 					Melder_throw (U"Early end of text detected in comment while looking for a real number (line ", MelderReadText_getLineNumber (me), U").");
126 				c = MelderReadText_getChar (me);
127 			}
128 		}
129 		for (i = 0; i < 40; i ++) {
130 			if (c > 127)
131 				Melder_throw (U"Found strange text while looking for a real number in text (line ", MelderReadText_getLineNumber (me), U").");
132 			buffer [i] = (char) (char8) c;   // guarded conversion down
133 			c = MelderReadText_getChar (me);
134 			if (c == U'\0') { break; }   // this may well be OK here
135 			if (Melder_isHorizontalOrVerticalSpace (c)) break;
136 		}
137 		if (i >= 40)
138 			Melder_throw (U"Found long text while searching for a real number in text (line ", MelderReadText_getLineNumber (me), U").");
139 	} while (i == 0 && buffer [0] == '+');   // guard against single '+' symbols, which occur in complex numbers
140 	buffer [i + 1] = '\0';
141 	slash = strchr (buffer, '/');
142 	if (slash) {
143 		*slash = '\0';
144 		double numerator = Melder_a8tof (buffer), denominator = Melder_a8tof (slash + 1);
145 		if (isundef (numerator) || isundef (denominator) || denominator == 0.0)
146 			return undefined;
147 		return numerator / denominator;
148 	}
149 	return Melder_a8tof (buffer);
150 }
151 
getComplex(MelderReadText me)152 static dcomplex getComplex (MelderReadText me) {
153 	dcomplex result;
154 	char realBuffer [41], imaginaryBuffer [41];
155 	integer ireal = 0, iimag = 0;
156 	char32 c;
157 	bool inExponent = false, inExponentNumber = false, separatorIsMinus = false;
158 	for (c = MelderReadText_getChar (me); c != U'-' && ! Melder_isAsciiDecimalNumber (c) && c != U'+'; c = MelderReadText_getChar (me)) {
159 		if (c == U'\0')
160 			Melder_throw (U"Early end of text detected while looking for a complex number (line ", MelderReadText_getLineNumber (me), U").");
161 		if (c == U'!') {   // end-of-line comment?
162 			while ((c = MelderReadText_getChar (me)) != U'\n' && c != U'\r') {
163 				if (c == U'\0')
164 					Melder_throw (U"Early end of text detected in comment while looking for a complex number (line ", MelderReadText_getLineNumber (me), U").");
165 			}
166 		}
167 		if (c == U'\"')
168 			Melder_throw (U"Found a string while looking for a complex number in text (line ", MelderReadText_getLineNumber (me), U").");
169 		if (c == U'<')
170 			Melder_throw (U"Found an enumerated value while looking for a complex number in text (line ", MelderReadText_getLineNumber (me), U").");
171 		while (! Melder_isHorizontalOrVerticalSpace (c)) {
172 			if (c == U'\0')
173 				Melder_throw (U"Early end of text detected in comment while looking for a complex number (line ", MelderReadText_getLineNumber (me), U").");
174 			c = MelderReadText_getChar (me);
175 		}
176 	}
177 	for (; ireal < 40; ireal ++) {
178 		if (c > 127)
179 			Melder_throw (U"Found strange text while looking for a complex number in text (line ", MelderReadText_getLineNumber (me), U").");
180 		if (inExponent) {
181 			if (c == U'+' || c == U'-')  {
182 				if (inExponentNumber) {
183 					/*
184 						This  must be the beginning of the imaginary part.
185 					*/
186 					separatorIsMinus = ( c == U'-' );
187 					realBuffer [ireal] = U'\0';
188 					break;
189 				} else {
190 					inExponentNumber = true;
191 				}
192 			} else if (Melder_isAsciiDecimalNumber (c)) {
193 				inExponentNumber = true;
194 			} else if (inExponentNumber) {   // typically a space
195 				realBuffer [ireal] = U'\0';
196 				break;
197 			} else {
198 				Melder_throw (U"Found unexpected symbol in the exponent of a complex number (line ", MelderReadText_getLineNumber (me), U").");
199 			}
200 		} else if (ireal > 0 && (c == U'+' || c == U'-')) {   // note: initial signs are not separators
201 			separatorIsMinus = ( c == U'-' );
202 			realBuffer [ireal] = U'\0';
203 			break;
204 		}
205 		if (c == 'e' || c == 'E')
206 			inExponent = true;
207 		realBuffer [ireal] = (char) (char8) c;   // guarded conversion down
208 		c = MelderReadText_getChar (me);
209 		if (c == U'\0')
210 			Melder_throw (U"Missing imaginary part in complex number (line ", MelderReadText_getLineNumber (me), U").");
211 		if (Melder_isHorizontalOrVerticalSpace (c))
212 			Melder_throw (U"Found a space within a complex number (line ", MelderReadText_getLineNumber (me), U").");
213 	}
214 	if (ireal >= 40)
215 		Melder_throw (U"Found long text while searching for a complex number in text (line ", MelderReadText_getLineNumber (me), U").");
216 	realBuffer [ireal + 1] = '\0';
217 	result. real (Melder_a8tof (realBuffer));
218 	c = MelderReadText_getChar (me);
219 	if (c != U'-' && ! Melder_isAsciiDecimalNumber (c) && c != U'+')
220 		Melder_throw (U"Found strange text while looking for the imaginary part of a complex number in text (line ", MelderReadText_getLineNumber (me), U").");
221 	if (c == U'\0')
222 		Melder_throw (U"Early end of text detected while looking for the imaginary part of a complex number (line ", MelderReadText_getLineNumber (me), U").");
223 	if (Melder_isHorizontalOrVerticalSpace (c))
224 		Melder_throw (U"Found a space within a complex number (line ", MelderReadText_getLineNumber (me), U").");
225 	if (c == U'!') {   // end-of-line comment?
226 		while ((c = MelderReadText_getChar (me)) != U'\n' && c != U'\r') {
227 			if (c == U'\0')
228 				Melder_throw (U"Early end of text detected in comment while looking for the imaginary part of a complex number (line ", MelderReadText_getLineNumber (me), U").");
229 		}
230 	}
231 	if (c == U'\"')
232 		Melder_throw (U"Found a string while looking for the imaginary part of a complex number in text (line ", MelderReadText_getLineNumber (me), U").");
233 	if (c == U'<')
234 		Melder_throw (U"Found an enumerated value while looking for the imaginary part of a complex number in text (line ", MelderReadText_getLineNumber (me), U").");
235 	for (; iimag < 40; iimag ++) {
236 		if (c > 127)
237 			Melder_throw (U"Found strange text while looking for the imaginary part of a complex number in text (line ", MelderReadText_getLineNumber (me), U").");
238 		imaginaryBuffer [iimag] = (char) (char8) c;   // guarded conversion down
239 		c = MelderReadText_getChar (me);
240 		if (c == U'\0')
241 			Melder_throw (U"Missing i in a complex number in text (line ", MelderReadText_getLineNumber (me), U").");
242 		if (Melder_isHorizontalOrVerticalSpace (c))
243 			Melder_throw (U"Missing i in a complex number in text (line ", MelderReadText_getLineNumber (me), U").");
244 		if (c == U'i')
245 			break;
246 	}
247 	if (iimag >= 40)
248 		Melder_throw (U"Found long text while searching for the imaginary part of a complex number in text (line ", MelderReadText_getLineNumber (me), U").");
249 	imaginaryBuffer [iimag + 1] = '\0';
250 	result. imag (Melder_a8tof (imaginaryBuffer) * ( separatorIsMinus ? -1.0 : 1.0 ));
251 	return result;
252 }
253 
getEnum(MelderReadText me,int (* getValue)(conststring32))254 static int getEnum (MelderReadText me, int (*getValue) (conststring32)) {
255 	char32 buffer [41], c;
256 	for (c = MelderReadText_getChar (me); c != U'<'; c = MelderReadText_getChar (me)) {
257 		if (c == U'\0')
258 			Melder_throw (U"Early end of text detected while looking for an enumerated value (line ", MelderReadText_getLineNumber (me), U").");
259 		if (c == U'!') {   /* End-of-line comment? */
260 			while ((c = MelderReadText_getChar (me)) != U'\n' && c != U'\r') {
261 				if (c == U'\0')
262 					Melder_throw (U"Early end of text detected in comment while looking for an enumerated value (line ", MelderReadText_getLineNumber (me), U").");
263 			}
264 		}
265 		if (c == U'-' || Melder_isAsciiDecimalNumber (c) || c == U'+')
266 			Melder_throw (U"Found a number while looking for an enumerated value in text (line ", MelderReadText_getLineNumber (me), U").");
267 		if (c == U'\"')
268 			Melder_throw (U"Found a string while looking for an enumerated value in text (line ", MelderReadText_getLineNumber (me), U").");
269 		while (! Melder_isHorizontalOrVerticalSpace (c)) {
270 			if (c == U'\0')
271 				Melder_throw (U"Early end of text detected in comment while looking for an enumerated value (line ", MelderReadText_getLineNumber (me), U").");
272 			c = MelderReadText_getChar (me);
273 		}
274 	}
275 	int i = 0;
276 	for (; i < 40; i ++) {
277 		c = MelderReadText_getChar (me);   // read past first '<'
278 		if (c == U'\0')
279 			Melder_throw (U"Early end of text detected while reading an enumerated value (line ", MelderReadText_getLineNumber (me), U").");
280 		constexpr char32 theOnlySpaceAllowedInAnEnum = U' ';
281 		if (Melder_isHorizontalOrVerticalSpace (c) && c != theOnlySpaceAllowedInAnEnum)
282 			Melder_throw (U"No matching '>' while reading an enumerated value (line ", MelderReadText_getLineNumber (me), U").");
283 		if (c == U'>')
284 			break;   // the expected closing bracket; not added to the buffer
285 		buffer [i] = c;
286 	}
287 	if (i >= 40)
288 		Melder_throw (U"Found strange text while reading an enumerated value in text (line ", MelderReadText_getLineNumber (me), U").");
289 	buffer [i] = U'\0';
290 	int value = getValue (buffer);
291 	if (value < 0)
292 		Melder_throw (U"\"", buffer, U"\" is not a value of the enumerated type.");
293 	return value;
294 }
295 
peekString(MelderReadText me)296 static char32 * peekString (MelderReadText me) {
297 	static MelderString buffer;
298 	MelderString_empty (& buffer);
299 	for (char32 c = MelderReadText_getChar (me); c != U'\"'; c = MelderReadText_getChar (me)) {
300 		if (c == U'\0')
301 			Melder_throw (U"Early end of text detected while looking for a string (line ", MelderReadText_getLineNumber (me), U").");
302 		if (c == U'!') {   // end-of-line comment?
303 			while ((c = MelderReadText_getChar (me)) != '\n' && c != '\r') {
304 				if (c == U'\0')
305 					Melder_throw (U"Early end of text detected in comment while looking for a string (line ", MelderReadText_getLineNumber (me), U").");
306 			}
307 		}
308 		if (c == U'-' || Melder_isAsciiDecimalNumber (c) || c == U'+')
309 			Melder_throw (U"Found a number while looking for a string in text (line ", MelderReadText_getLineNumber (me), U").");
310 		if (c == U'<')
311 			Melder_throw (U"Found an enumerated value while looking for a string in text (line ", MelderReadText_getLineNumber (me), U").");
312 		while (! Melder_isHorizontalOrVerticalSpace (c)) {
313 			if (c == U'\0')
314 				Melder_throw (U"Early end of text detected while looking for a string (line ", MelderReadText_getLineNumber (me), U").");
315 			c = MelderReadText_getChar (me);
316 		}
317 	}
318 	for (int i = 0; 1; i ++) {
319 		char32 c = MelderReadText_getChar (me);   // read past first '"'
320 		if (c == U'\0')
321 			Melder_throw (U"Early end of text detected while reading a string (line ", MelderReadText_getLineNumber (me), U").");
322 		if (c == U'\"') {
323 			char32 next = MelderReadText_getChar (me);
324 			if (next == U'\0') { break; }   // closing quote is last character in file: OK
325 			if (next != U'\"') {
326 				if (Melder_isHorizontalOrVerticalSpace (next)) {
327 					// closing quote is followed by whitespace: it is OK to skip this whitespace (no need to "ungetChar")
328 				} else {
329 					char32 kar2 [2] = { next, U'\0' };
330 					Melder_throw (U"Character ", kar2, U" following quote (line ", MelderReadText_getLineNumber (me), U"). End of string or undoubled quote?");
331 				}
332 				break;   // the expected closing double quote; not added to the buffer
333 			}   // else: add only one of the two quotes to the buffer
334 		}
335 		MelderString_appendCharacter (& buffer, c);
336 	}
337 	return buffer. string;
338 }
339 
340 #include "enums_getText.h"
341 #include "abcio_enums.h"
342 #include "enums_getValue.h"
343 #include "abcio_enums.h"
344 
texgeti8(MelderReadText text)345 int texgeti8 (MelderReadText text) {
346 	try {
347 		int64 externalValue = getInteger (text);
348 		if (externalValue < INT8_MIN || externalValue > INT8_MAX)
349 			Melder_throw (U"Value (", externalValue, U") out of range (-128 .. +127).");
350 		return (int) externalValue;
351 	} catch (MelderError) {
352 		Melder_throw (U"Signed small integer not read from text file.");
353 	}
354 }
355 
texgeti16(MelderReadText text)356 int16 texgeti16 (MelderReadText text) {
357 	try {
358 		int64 externalValue = getInteger (text);
359 		if (externalValue < INT16_MIN || externalValue > INT16_MAX)
360 			Melder_throw (U"Value (", externalValue, U") out of range (-32768 .. +32767).");
361 		return (int16) externalValue;
362 	} catch (MelderError) {
363 		Melder_throw (U"Signed short integer not read from text file.");
364 	}
365 }
366 
texgeti32(MelderReadText text)367 int32 texgeti32 (MelderReadText text) {
368 	try {
369 		int64 externalValue = getInteger (text);
370 		if (externalValue < INT32_MIN || externalValue > INT32_MAX)
371 			Melder_throw (U"Value (", externalValue, U") out of range (-2147483648 .. +2147483647).");
372 		return (int32) externalValue;
373 	} catch (MelderError) {
374 		Melder_throw (U"Signed integer not read from text file.");
375 	}
376 }
377 
texgetinteger(MelderReadText text)378 integer texgetinteger (MelderReadText text) {
379 	try {
380 		int64 externalValue = getInteger (text);
381 		if (externalValue < INT32_MIN || externalValue > INT32_MAX)
382 			Melder_throw (U"Value (", externalValue, U") out of range (-2147483648 .. +2147483647).");   // this will change
383 		return (integer) externalValue;
384 	} catch (MelderError) {
385 		Melder_throw (U"Signed integer not read from text file.");
386 	}
387 }
388 
texgetu8(MelderReadText text)389 unsigned int texgetu8 (MelderReadText text) {
390 	try {
391 		uint64 externalValue = getUnsigned (text);
392 		if (externalValue > UINT8_MAX)
393 			Melder_throw (U"Value (", externalValue, U") out of range (0 .. 255).");
394 		return (unsigned int) externalValue;
395 	} catch (MelderError) {
396 		Melder_throw (U"Unsigned small integer not read from text file.");
397 	}
398 }
399 
texgetu16(MelderReadText text)400 uint16 texgetu16 (MelderReadText text) {
401 	try {
402 		uint64 externalValue = getUnsigned (text);
403 		if (externalValue > UINT16_MAX)
404 			Melder_throw (U"Value (", externalValue, U") out of range (0 .. 65535).");
405 		return (uint16) externalValue;
406 	} catch (MelderError) {
407 		Melder_throw (U"Unsigned short integer not read from text file.");
408 	}
409 }
410 
texgetu32(MelderReadText text)411 uint32 texgetu32 (MelderReadText text) {
412 	try {
413 		uint64 externalValue = getUnsigned (text);
414 		if (externalValue > UINT32_MAX)
415 			Melder_throw (U"Value (", externalValue, U") out of range (0 .. 4294967295).");
416 		return (uint32) externalValue;
417 	} catch (MelderError) {
418 		Melder_throw (U"Unsigned integer not read from text file.");
419 	}
420 }
421 
texgetr32(MelderReadText text)422 double texgetr32 (MelderReadText text) { return getReal (text); }
texgetr64(MelderReadText text)423 double texgetr64 (MelderReadText text) { return getReal (text); }
texgetr80(MelderReadText text)424 double texgetr80 (MelderReadText text) { return getReal (text); }
texgetc64(MelderReadText text)425 dcomplex texgetc64  (MelderReadText text) { return getComplex (text); }
texgetc128(MelderReadText text)426 dcomplex texgetc128 (MelderReadText text) { return getComplex (text); }
427 
texgete8(MelderReadText text,enum_generic_getValue getValue)428 int texgete8 (MelderReadText text, enum_generic_getValue getValue) { return getEnum (text, getValue); }
texgete16(MelderReadText text,enum_generic_getValue getValue)429 int texgete16 (MelderReadText text, enum_generic_getValue getValue) { return getEnum (text, getValue); }
texgeteb(MelderReadText text)430 bool texgeteb (MelderReadText text) { return getEnum (text, (enum_generic_getValue) kBoolean_getValue); }
texgeteq(MelderReadText text)431 bool texgeteq (MelderReadText text) { return getEnum (text, (enum_generic_getValue) kQuestion_getValue); }
texgetex(MelderReadText text)432 bool texgetex (MelderReadText text) { return getEnum (text, (enum_generic_getValue) kExistence_getValue); }
texgetw16(MelderReadText text)433 autostring32 texgetw16 (MelderReadText text) { return Melder_dup (peekString (text)); }
texgetw32(MelderReadText text)434 autostring32 texgetw32 (MelderReadText text) { return Melder_dup (peekString (text)); }
435 
texindent(MelderFile file)436 void texindent (MelderFile file) { file -> indent += 4; }
texexdent(MelderFile file)437 void texexdent (MelderFile file) { file -> indent -= 4; }
texresetindent(MelderFile file)438 void texresetindent (MelderFile file) { file -> indent = 0; }
439 
440 #define texput_UP_TO_NINE_NULLABLE_STRINGS  \
441 	conststring32 s1, conststring32 s2, conststring32 s3, \
442 	conststring32 s4, conststring32 s5, conststring32 s6, \
443 	conststring32 s7, conststring32 s8, conststring32 s9
444 
texputintro(MelderFile file,texput_UP_TO_NINE_NULLABLE_STRINGS)445 void texputintro (MelderFile file, texput_UP_TO_NINE_NULLABLE_STRINGS) {
446 	if (file -> verbose) {
447 		MelderFile_write (file, U"\n");
448 		for (int iindent = 1; iindent <= file -> indent; iindent ++) {
449 			MelderFile_write (file, U" ");
450 		}
451 		MelderFile_write (file,
452 			s1 && s1 [0] == U'd' && s1 [1] == U'_' ? & s1 [2] : & s1 [0],
453 			s2 && s2 [0] == U'd' && s2 [1] == U'_' ? & s2 [2] : & s2 [0],
454 			s3 && s3 [0] == U'd' && s3 [1] == U'_' ? & s3 [2] : & s3 [0],
455 			s4 && s4 [0] == U'd' && s4 [1] == U'_' ? & s4 [2] : & s4 [0],
456 			s5 && s5 [0] == U'd' && s5 [1] == U'_' ? & s5 [2] : & s5 [0],
457 			s6 && s6 [0] == U'd' && s6 [1] == U'_' ? & s6 [2] : & s6 [0],
458 			s7 && s7 [0] == U'd' && s7 [1] == U'_' ? & s7 [2] : & s7 [0],
459 			s8 && s8 [0] == U'd' && s8 [1] == U'_' ? & s8 [2] : & s8 [0],
460 			s9 && s9 [0] == U'd' && s9 [1] == U'_' ? & s9 [2] : & s9 [0]);
461 	}
462 	file -> indent += 4;
463 }
464 
465 #define PUTLEADER  \
466 	MelderFile_write (file, U"\n"); \
467 	if (file -> verbose) { \
468 		for (int iindent = 1; iindent <= file -> indent; iindent ++) { \
469 			MelderFile_write (file, U" "); \
470 		} \
471 		MelderFile_write (file, \
472 			s1 && s1 [0] == U'd' && s1 [1] == U'_' ? & s1 [2] : & s1 [0], \
473 			s2 && s2 [0] == U'd' && s2 [1] == U'_' ? & s2 [2] : & s2 [0], \
474 			s3 && s3 [0] == U'd' && s3 [1] == U'_' ? & s3 [2] : & s3 [0], \
475 			s4 && s4 [0] == U'd' && s4 [1] == U'_' ? & s4 [2] : & s4 [0], \
476 			s5 && s5 [0] == U'd' && s5 [1] == U'_' ? & s5 [2] : & s5 [0], \
477 			s6 && s6 [0] == U'd' && s6 [1] == U'_' ? & s6 [2] : & s6 [0], \
478 			s7 && s7 [0] == U'd' && s7 [1] == U'_' ? & s7 [2] : & s7 [0], \
479 			s8 && s8 [0] == U'd' && s8 [1] == U'_' ? & s8 [2] : & s8 [0], \
480 			s9 && s9 [0] == U'd' && s9 [1] == U'_' ? & s9 [2] : & s9 [0]); \
481 	}
482 
texputi8(MelderFile file,int i,texput_UP_TO_NINE_NULLABLE_STRINGS)483 void texputi8 (MelderFile file, int i, texput_UP_TO_NINE_NULLABLE_STRINGS) {
484 	PUTLEADER
485 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, i, file -> verbose ? U" " : nullptr);
486 }
texputi16(MelderFile file,int i,texput_UP_TO_NINE_NULLABLE_STRINGS)487 void texputi16 (MelderFile file, int i, texput_UP_TO_NINE_NULLABLE_STRINGS) {
488 	PUTLEADER
489 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, i, file -> verbose ? U" " : nullptr);
490 }
texputi32(MelderFile file,long i,texput_UP_TO_NINE_NULLABLE_STRINGS)491 void texputi32 (MelderFile file, long i, texput_UP_TO_NINE_NULLABLE_STRINGS) {
492 	PUTLEADER
493 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, i, file -> verbose ? U" " : nullptr);
494 }
texputinteger(MelderFile file,integer number,texput_UP_TO_NINE_NULLABLE_STRINGS)495 void texputinteger (MelderFile file, integer number, texput_UP_TO_NINE_NULLABLE_STRINGS) {
496 	PUTLEADER
497 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, number, file -> verbose ? U" " : nullptr);
498 }
texputu8(MelderFile file,unsigned int u,texput_UP_TO_NINE_NULLABLE_STRINGS)499 void texputu8 (MelderFile file, unsigned int u, texput_UP_TO_NINE_NULLABLE_STRINGS) {
500 	PUTLEADER
501 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, u, file -> verbose ? U" " : nullptr);
502 }
texputu16(MelderFile file,unsigned int u,texput_UP_TO_NINE_NULLABLE_STRINGS)503 void texputu16 (MelderFile file, unsigned int u, texput_UP_TO_NINE_NULLABLE_STRINGS) {
504 	PUTLEADER
505 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, u, file -> verbose ? U" " : nullptr);
506 }
texputu32(MelderFile file,unsigned long u,texput_UP_TO_NINE_NULLABLE_STRINGS)507 void texputu32 (MelderFile file, unsigned long u, texput_UP_TO_NINE_NULLABLE_STRINGS) {
508 	PUTLEADER
509 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, u, file -> verbose ? U" " : nullptr);
510 }
texputr32(MelderFile file,double x,texput_UP_TO_NINE_NULLABLE_STRINGS)511 void texputr32 (MelderFile file, double x, texput_UP_TO_NINE_NULLABLE_STRINGS) {
512 	PUTLEADER
513 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, Melder_single (x), file -> verbose ? U" " : nullptr);
514 }
texputr64(MelderFile file,double x,texput_UP_TO_NINE_NULLABLE_STRINGS)515 void texputr64 (MelderFile file, double x, texput_UP_TO_NINE_NULLABLE_STRINGS) {
516 	PUTLEADER
517 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, x, file -> verbose ? U" " : nullptr);
518 }
texputc64(MelderFile file,dcomplex z,texput_UP_TO_NINE_NULLABLE_STRINGS)519 void texputc64 (MelderFile file, dcomplex z, texput_UP_TO_NINE_NULLABLE_STRINGS) {
520 	PUTLEADER
521 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, z, file -> verbose ? U" " : nullptr);
522 }
texputc128(MelderFile file,dcomplex z,texput_UP_TO_NINE_NULLABLE_STRINGS)523 void texputc128 (MelderFile file, dcomplex z, texput_UP_TO_NINE_NULLABLE_STRINGS) {
524 	PUTLEADER
525 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, z, file -> verbose ? U" " : nullptr);
526 }
texpute8(MelderFile file,int i,conststring32 (* getText)(int),texput_UP_TO_NINE_NULLABLE_STRINGS)527 void texpute8 (MelderFile file, int i, conststring32 (*getText) (int), texput_UP_TO_NINE_NULLABLE_STRINGS) {
528 	PUTLEADER
529 	MelderFile_write (file, file -> verbose ? U" = <" : U"<", getText (i), file -> verbose ? U"> " : U">");
530 }
texpute16(MelderFile file,int i,conststring32 (* getText)(int),texput_UP_TO_NINE_NULLABLE_STRINGS)531 void texpute16 (MelderFile file, int i, conststring32 (*getText) (int), texput_UP_TO_NINE_NULLABLE_STRINGS) {
532 	PUTLEADER
533 	MelderFile_write (file, file -> verbose ? U" = <" : U"<", getText (i), file -> verbose ? U"> " : U">");
534 }
texputeb(MelderFile file,bool i,texput_UP_TO_NINE_NULLABLE_STRINGS)535 void texputeb (MelderFile file, bool i, texput_UP_TO_NINE_NULLABLE_STRINGS) {
536 	PUTLEADER
537 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, i ? U"<true>" : U"<false>", file -> verbose ? U" " : nullptr);
538 }
texputeq(MelderFile file,bool i,texput_UP_TO_NINE_NULLABLE_STRINGS)539 void texputeq (MelderFile file, bool i, texput_UP_TO_NINE_NULLABLE_STRINGS) {
540 	PUTLEADER
541 	MelderFile_write (file, file -> verbose ? U"? " : nullptr, i ? U"<yes>" : U"<no>", file -> verbose ? U" " : nullptr);
542 }
texputex(MelderFile file,bool i,texput_UP_TO_NINE_NULLABLE_STRINGS)543 void texputex (MelderFile file, bool i, texput_UP_TO_NINE_NULLABLE_STRINGS) {
544 	PUTLEADER
545 	MelderFile_write (file, file -> verbose ? U"? " : nullptr, i ? U"<exists>" : U"<absent>", file -> verbose ? U" " : nullptr);
546 }
texputs8(MelderFile file,const char * s,texput_UP_TO_NINE_NULLABLE_STRINGS)547 void texputs8 (MelderFile file, const char *s, texput_UP_TO_NINE_NULLABLE_STRINGS) {
548 	PUTLEADER
549 	MelderFile_write (file, file -> verbose ? U" = \"" : U"\"");
550 	if (s) {
551 		char c;
552 		while ((c = *s ++) != '\0') {
553 			MelderFile_writeCharacter (file, (char32) (char8) c);
554 			if (c == U'\"') MelderFile_writeCharacter (file, (char32) (char8) c);   // double any internal quotes
555 		}
556 	}
557 	MelderFile_write (file, file -> verbose ? U"\" " : U"\"");
558 }
texputs16(MelderFile file,const char * s,texput_UP_TO_NINE_NULLABLE_STRINGS)559 void texputs16 (MelderFile file, const char *s, texput_UP_TO_NINE_NULLABLE_STRINGS) {
560 	PUTLEADER
561 	MelderFile_write (file, file -> verbose ? U" = \"" : U"\"");
562 	if (s) {
563 		char c;
564 		while ((c = *s ++) != '\0') {
565 			MelderFile_writeCharacter (file, (char32) (char8) c);
566 			if (c == '\"') MelderFile_writeCharacter (file, (char32) (char8) c);   // double any internal quotes
567 		}
568 	}
569 	MelderFile_write (file, file -> verbose ? U"\" " : U"\"");
570 }
texputs32(MelderFile file,const char * s,texput_UP_TO_NINE_NULLABLE_STRINGS)571 void texputs32 (MelderFile file, const char *s, texput_UP_TO_NINE_NULLABLE_STRINGS) {
572 	PUTLEADER
573 	MelderFile_write (file, file -> verbose ? U" = \"" : U"\"");
574 	if (s) {
575 		char c;
576 		while ((c = *s ++) != '\0') {
577 			MelderFile_writeCharacter (file, (char32) (char8) c);
578 			if (c == '\"') MelderFile_writeCharacter (file, (char32) (char8) c);   // double any internal quotes
579 		}
580 	}
581 	MelderFile_write (file, file -> verbose ? U"\" " : U"\"");
582 }
texputw16(MelderFile file,conststring32 s,texput_UP_TO_NINE_NULLABLE_STRINGS)583 void texputw16 (MelderFile file, conststring32 s, texput_UP_TO_NINE_NULLABLE_STRINGS) {
584 	PUTLEADER
585 	MelderFile_write (file, file -> verbose ? U" = \"" : U"\"");
586 	if (s) {
587 		char32 c;
588 		while ((c = *s ++) != U'\0') {
589 			MelderFile_writeCharacter (file, c);
590 			if (c == U'\"') MelderFile_writeCharacter (file, c);   // double any internal quotes
591 		}
592 	}
593 	MelderFile_write (file, file -> verbose ? U"\" " : U"\"");
594 }
texputw32(MelderFile file,conststring32 s,texput_UP_TO_NINE_NULLABLE_STRINGS)595 void texputw32 (MelderFile file, conststring32 s, texput_UP_TO_NINE_NULLABLE_STRINGS) {
596 	PUTLEADER
597 	MelderFile_write (file, file -> verbose ? U" = \"" : U"\"");
598 	if (s) {
599 		char32 c;
600 		while ((c = *s ++) != U'\0') {
601 			MelderFile_writeCharacter (file, c);
602 			if (c == U'\"') MelderFile_writeCharacter (file, c);   // double any internal quotes
603 		}
604 	}
605 	MelderFile_write (file, file -> verbose ? U"\" " : U"\"");
606 }
607 
608 /********** machine-independent binary I/O **********/
609 
610 /* Optimizations for machines for which some of the formats are native. */
611 
612 /* On which machines is "short" a two's complement Big-Endian (MSB-first) 2-byte word? */
613 
614 #if defined (macintosh) && TARGET_RT_BIG_ENDIAN == 1
615 	#define binario_16bitBE 1
616 	#define binario_16bitLE 0
617 #elif defined (_WIN32) || defined (macintosh) && TARGET_RT_LITTLE_ENDIAN == 1
618 	#define binario_16bitBE 0
619 	#define binario_16bitLE 1
620 #else
621 	#define binario_16bitBE 0
622 	#define binario_16bitLE 0
623 #endif
624 
625 /* On which machines is "long" a two's complement Big-Endian (MSB-first) 4-byte word? */
626 
627 #if defined (macintosh) && TARGET_RT_BIG_ENDIAN == 1
628 	#define binario_32bitBE 1
629 	#define binario_32bitLE 0
630 #elif defined (_WIN32) || defined (macintosh) && TARGET_RT_LITTLE_ENDIAN == 1
631 	#define binario_32bitBE 0
632 	#define binario_32bitLE 1
633 #else
634 	#define binario_32bitBE 0
635 	#define binario_32bitLE 0
636 #endif
637 
638 /* On which machines is "float" IEEE, four bytes, Most Significant Bit first? */
639 
640 #if defined (macintosh) && TARGET_RT_BIG_ENDIAN == 1
641 	#define binario_floatIEEE4msb (sizeof (float) == 4)
642 	#define binario_floatIEEE4lsb 0
643 #elif defined (_WIN32) || defined (macintosh) && TARGET_RT_LITTLE_ENDIAN == 1
644 	#define binario_floatIEEE4msb 0
645 	#define binario_floatIEEE4lsb (sizeof (float) == 4)
646 #else
647 	#define binario_floatIEEE4msb 0
648 	#define binario_floatIEEE4lsb 0
649 #endif
650 
651 /* On which machines is "double" IEEE, eight bytes, Most Significant Bit first? */
652 
653 #if defined (macintosh) && TARGET_RT_BIG_ENDIAN == 1
654 	#define binario_doubleIEEE8msb (sizeof (double) == 8)
655 	#define binario_doubleIEEE8lsb 0
656 #elif defined (_WIN32) || defined (macintosh) && TARGET_RT_LITTLE_ENDIAN == 1
657 	#define binario_doubleIEEE8msb 0
658 	#define binario_doubleIEEE8lsb (sizeof (double) == 8)
659 #else
660 	#define binario_doubleIEEE8msb 0
661 	#define binario_doubleIEEE8lsb 0
662 #endif
663 
664 /*
665 	The routines bingetr32, bingetr64, binputr32, and binputr64,
666 	were implemented by Paul Boersma from the descriptions of the IEEE floating-point formats,
667 	as found in the MC68881/MC68882 User's Manual by Motorola (second edition, 1989).
668 	The following copyright notice only refers to the code of bingetr10 and binputr10.
669 */
670 
671 /* Copyright (C) 1988-1991 Apple Computer, Inc.
672  * All rights reserved.
673  *
674  * Warranty Information
675  *  Even though Apple has reviewed this software, Apple makes no warranty
676  *  or representation, either express or implied, with respect to this
677  *  software, its quality, accuracy, merchantability, or fitness for a
678  *  particular purpose.  As a result, this software is provided "as is,"
679  *  and you, its user, are assuming the entire risk as to its quality
680  *  and accuracy.
681  *
682  * This code may be used and freely distributed as long as it includes
683  * this copyright notice and the above warranty information.
684  *
685  * Machine-independent I/O routines for IEEE floating-point numbers.
686  *
687  * NaN's and infinities are converted to HUGE_VAL or HUGE, which
688  * happens to be infinity on IEEE machines.  Unfortunately, it is
689  * impossible to preserve NaN's in a machine-independent way.
690  * Infinities are, however, preserved on IEEE machines.
691  *
692  * These routines have been tested on the following machines:
693  *    Apple Macintosh, MPW 3.1 C compiler
694  *    Apple Macintosh, THINK C compiler
695  *    Silicon Graphics IRIS, MIPS compiler
696  *    Cray X/MP and Y/MP
697  *    Digital Equipment VAX
698  *
699  * Implemented by Malcolm Slaney and Ken Turkowski.
700  *
701  * Malcolm Slaney contributions during 1988-1990 include big- and little-
702  * endian file I/O, conversion to and from Motorola's extended 80-bit
703  * floating-point format, and conversions to and from IEEE single-
704  * precision floating-point format.
705  *
706  * In 1991, Ken Turkowski implemented the conversions to and from
707  * IEEE double-precision format, added more precision to the extended
708  * conversions, and accommodated conversions involving +/- infinity,
709  * NaN's, and denormalized numbers.
710  */
711 
712 // QUESTION: do the following work correctly if a long is 64 bits?
713 
714 # define FloatToUnsigned(f)  \
715 	 ((unsigned long)(((long)((f) - 2147483648.0)) + 2147483647L + 1))
716 
717 # define UnsignedToFloat(u)    \
718 	  (((double)((long)((u) - 2147483647L - 1))) + 2147483648.0)
719 
720 //#define FloatToUnsigned(f) (uint32) (f)
721 //#define UnsignedToFloat(u) (double) (u)
722 
723 /****************************************************************
724  * Extended precision IEEE floating-point conversion routines.
725  ****************************************************************/
726 
727 /*
728  * C O N V E R T   T O   I E E E   E X T E N D E D
729  */
730 
731 /*
732  * C O N V E R T   F R O M   I E E E   E X T E N D E D
733  */
734 
735 /*************** End of Apple Computer intermezzo. ***************/
736 
readError(FILE * f,conststring32 text)737 static void readError (FILE *f, conststring32 text) {
738 	Melder_throw (feof (f) ? U"Reached end of file" : U"Error in file", U" while trying to read ", text);
739 }
740 
writeError(conststring32 text)741 static void writeError (conststring32 text) {
742 	Melder_throw (U"Error in file while trying to write ", text);
743 }
744 
bingetu8(FILE * f)745 unsigned int bingetu8 (FILE *f) {
746 	try {
747 		int externalValue = getc (f);   // either -1 (EOF) or in the range 0..255
748 		if (externalValue < 0) readError (f, U"a byte.");
749 		return (unsigned int) externalValue;
750 	} catch (MelderError) {
751 		Melder_throw (U"Unsigned integer not read from 1 byte in binary file.");
752 	}
753 }
754 
binputu8(unsigned int u,FILE * f)755 void binputu8 (unsigned int u, FILE *f) {
756 	try {
757 		if (putc ((int) u, f) < 0) writeError (U"a byte.");
758 	} catch (MelderError) {
759 		Melder_throw (U"Unsigned integer not written to 1 byte in binary file.");
760 	}
761 }
762 
bingeti8(FILE * f)763 int bingeti8 (FILE *f) {
764 	try {
765 		int externalValue = getc (f);
766 		if (externalValue < 0) readError (f, U"a byte.");
767 		return (signed char) externalValue;   // this converts e.g. 200 to -56
768 	} catch (MelderError) {
769 		Melder_throw (U"Signed integer not read from 1 byte in binary file.");
770 	}
771 }
772 
binputi8(int u,FILE * f)773 void binputi8 (int u, FILE *f) {
774 	try {
775 		if (putc (u, f) < 0) writeError (U"a byte.");
776 	} catch (MelderError) {
777 		Melder_throw (U"Signed integer not written to 1 byte in binary file.");
778 	}
779 }
780 
bingetbool8(FILE * f)781 bool bingetbool8 (FILE *f) {
782 	try {
783 		int externalValue = getc (f);
784 		if (externalValue < 0) readError (f, U"a byte.");
785 		return (bool) externalValue;   // this converts e.g. 200 to true
786 	} catch (MelderError) {
787 		Melder_throw (U"Boolean not read from 1 byte in binary file.");
788 	}
789 }
790 
binputbool8(bool value,FILE * f)791 void binputbool8 (bool value, FILE *f) {
792 	try {
793 		if (putc (value, f) < 0) writeError (U"a byte.");
794 	} catch (MelderError) {
795 		Melder_throw (U"Boolean not written to 1 byte in binary file.");
796 	}
797 }
798 
bingete8(FILE * f,int min,int max,conststring32 type)799 int bingete8 (FILE *f, int min, int max, conststring32 type) {
800 	try {
801 		int externalValue = getc (f);
802 		if (externalValue < 0) readError (f, U"a byte.");
803 		int result = (signed char) externalValue;   // this converts e.g. 200 to -56, so the enumerated type is signed
804 		if (result < min || result > max)
805 			Melder_throw (result, U" is not a value of enumerated type <", type, U">.");
806 		return result;
807 	} catch (MelderError) {
808 		Melder_throw (U"Enumerated type not read from 1 byte in binary file.");
809 	}
810 }
811 
binpute8(int value,FILE * f)812 void binpute8 (int value, FILE *f) {
813 	try {
814 		if (putc (value, f) < 0) writeError (U"a byte.");
815 	} catch (MelderError) {
816 		Melder_throw (U"Enumerated type not written to 1 byte in binary file.");
817 	}
818 }
819 
820 static int bitsInReadBuffer = 0;
821 static unsigned char readBuffer;
822 
823 #define macro_bingetb(nbits) \
824 unsigned int bingetb##nbits (FILE *f) { \
825 	if (bitsInReadBuffer < nbits) { \
826 		int externalValue = fgetc (f); \
827 		if (externalValue < 0) readError (f, U"a bit."); \
828 		readBuffer = (unsigned char) externalValue; \
829 		bitsInReadBuffer = 8; \
830 	} \
831 	unsigned char result = (unsigned char) ((uint32) readBuffer << (8 - bitsInReadBuffer)); \
832 	bitsInReadBuffer -= nbits; \
833 	return result >> (8 - nbits); \
834 }
835 macro_bingetb (1)
836 macro_bingetb (2)
837 macro_bingetb (3)
838 macro_bingetb (4)
839 macro_bingetb (5)
840 macro_bingetb (6)
841 macro_bingetb (7)
842 
bingetb(FILE * f)843 void bingetb (FILE *f) { (void) f; bitsInReadBuffer = 0; }
844 
bingeti16(FILE * f)845 int16 bingeti16 (FILE *f) {
846 	try {
847 		if (binario_16bitBE && Melder_debug != 18) {
848 			int16 s;
849 			if (fread (& s, sizeof (int16), 1, f) != 1) readError (f, U"a signed 16-bit integer.");
850 			return s;
851 		} else {
852 			uint8 bytes [2];
853 			if (fread (bytes, sizeof (uint8), 2, f) != 2) readError (f, U"two bytes.");
854 			return (int16)   // reinterpret sign bit
855 				((uint16) ((uint16) bytes [0] << 8) |
856 						   (uint16) bytes [1]);
857 		}
858 	} catch (MelderError) {
859 		Melder_throw (U"Signed integer not read from 2 bytes in binary file.");
860 	}
861 }
862 
bingeti16LE(FILE * f)863 int16 bingeti16LE (FILE *f) {
864 	try {
865 		if (binario_16bitLE && Melder_debug != 18) {
866 			int16 s;
867 			if (fread (& s, sizeof (int16), 1, f) != 1) readError (f, U"a signed 16-bit integer.");
868 			return s;
869 		} else {
870 			uint8 bytes [2];
871 			if (fread (bytes, sizeof (uint8), 2, f) != 2) readError (f, U"two bytes.");
872 			return (int16)   // reinterpret sign bit
873 				((uint16) ((uint16) bytes [1] << 8) |
874 						   (uint16) bytes [0]);
875 		}
876 	} catch (MelderError) {
877 		Melder_throw (U"Signed integer not read from 2 bytes in binary file.");
878 	}
879 }
880 
bingetinteger16BE(FILE * f)881 integer bingetinteger16BE (FILE *f) {
882 	try {
883 		if (binario_16bitBE && Melder_debug != 18) {
884 			int16 s;
885 			if (fread (& s, sizeof (int16), 1, f) != 1) readError (f, U"a signed 16-bit integer.");
886 			return s;
887 		} else {
888 			uint8 bytes [2];
889 			if (fread (bytes, sizeof (uint8), 2, f) != 2) readError (f, U"two bytes.");
890 			return (int16)   // reinterpret sign bit
891 				((uint16) ((uint16) bytes [0] << 8) |
892 						   (uint16) bytes [1]);
893 		}
894 	} catch (MelderError) {
895 		Melder_throw (U"Signed integer not read from 2 bytes in binary file.");
896 	}
897 }
898 
bingetu16(FILE * f)899 uint16 bingetu16 (FILE *f) {
900 	try {
901 		if (binario_16bitBE && Melder_debug != 18) {
902 			uint16 s;
903 			if (fread (& s, sizeof (uint16), 1, f) != 1) readError (f, U"an unsigned 16-bit integer.");
904 			return s;   // without sign extension
905 		} else {
906 			uint8 bytes [2];
907 			if (fread (bytes, sizeof (uint8), 2, f) != 2) readError (f, U"two bytes.");
908 			return
909 				(uint16) ((uint16) bytes [0] << 8) |
910 						  (uint16) bytes [1];
911 		}
912 	} catch (MelderError) {
913 		Melder_throw (U"Unsigned integer not read from 2 bytes in binary file.");
914 	}
915 }
916 
bingetu16LE(FILE * f)917 uint16 bingetu16LE (FILE *f) {
918 	try {
919 		if (binario_16bitLE && Melder_debug != 18) {
920 			uint16 s;
921 			if (fread (& s, sizeof (uint16), 1, f) != 1) readError (f, U"an unsigned 16-bit integer.");
922 			return s;   // without sign extension
923 		} else {
924 			uint8 bytes [2];
925 			if (fread (bytes, sizeof (uint8), 2, f) != 2) readError (f, U"two bytes.");
926 			return
927 				(uint16) ((uint16) bytes [1] << 8) |
928 						  (uint16) bytes [0];
929 		}
930 	} catch (MelderError) {
931 		Melder_throw (U"Unsigned integer not read from 2 bytes in binary file.");
932 	}
933 }
934 
bingete16(FILE * f,int min,int max,conststring32 type)935 int bingete16 (FILE *f, int min, int max, conststring32 type) {
936 	try {
937 		int16 result;
938 		if (binario_16bitBE && Melder_debug != 18) {
939 			if (fread (& result, sizeof (int16), 1, f) != 1) readError (f, U"a signed 16-bit integer.");
940 		} else {
941 			uint8 bytes [2];
942 			if (fread (bytes, sizeof (uint8), 2, f) != 2) readError (f, U"two bytes.");
943 			uint16 externalValue =
944 				(uint16) ((uint16) bytes [0] << 8) |
945 						  (uint16) bytes [1];
946 			result = (int16) externalValue;
947 		}
948 		if (result < min || result > max)
949 			Melder_throw (result, U" is not a value of enumerated type \"", type, U"\".");
950 		return (int) result;
951 	} catch (MelderError) {
952 		Melder_throw (U"Enumerated value not read from 2 bytes in binary file.");
953 	}
954 }
955 
bingeti24(FILE * f)956 int32 bingeti24 (FILE *f) {
957 	try {
958 		uint8 bytes [3];
959 		if (fread (bytes, sizeof (uint8), 3, f) != 3) readError (f, U"three bytes.");
960 		uint32 externalValue =
961 			(uint32) ((uint32) bytes [0] << 16) |
962 			(uint32) ((uint32) bytes [1] << 8) |
963 					  (uint32) bytes [2];
964 		if ((bytes [0] & 128) != 0)   // is the 24-bit sign bit on?
965 			externalValue |= 0xFF00'0000;   // extend negative sign to 32 bits
966 		return (int32) externalValue;   // reinterpret sign bit
967 	} catch (MelderError) {
968 		Melder_throw (U"Signed integer not read from 3 bytes in binary file.");
969 	}
970 }
971 
bingeti24LE(FILE * f)972 int32 bingeti24LE (FILE *f) {
973 	try {
974 		uint8 bytes [3];
975 		if (fread (bytes, sizeof (uint8), 3, f) != 3) readError (f, U"three bytes.");
976 		uint32 externalValue =
977 			(uint32) ((uint32) bytes [2] << 16) |
978 			(uint32) ((uint32) bytes [1] << 8) |
979 					  (uint32) bytes [0];
980 		if ((bytes [2] & 128) != 0)   // is the 24-bit sign bit on?
981 			externalValue |= 0xFF00'0000;   // extend negative sign to 32 bits
982 		return (int32) externalValue;   // reinterpret sign bit
983 	} catch (MelderError) {
984 		Melder_throw (U"Signed integer not read from 3 bytes in binary file.");
985 	}
986 }
987 
bingeti32(FILE * f)988 int32 bingeti32 (FILE *f) {
989 	try {
990 		if (binario_32bitBE && Melder_debug != 18) {
991 			int32 l;
992 			if (fread (& l, sizeof (int32), 1, f) != 1) readError (f, U"a signed 32-bit integer.");
993 			return l;
994 		} else {
995 			uint8 bytes [4];
996 			if (fread (bytes, sizeof (uint8), 4, f) != 4) readError (f, U"four bytes.");
997 			return (int32)
998 				((uint32) ((uint32) bytes [0] << 24) |
999 				 (uint32) ((uint32) bytes [1] << 16) |
1000 				 (uint32) ((uint32) bytes [2] << 8) |
1001 						   (uint32) bytes [3]);
1002 		}
1003 	} catch (MelderError) {
1004 		Melder_throw (U"Signed integer not read from 4 bytes in binary file.");
1005 	}
1006 }
1007 
bingeti32LE(FILE * f)1008 int32 bingeti32LE (FILE *f) {
1009 	try {
1010 		if (binario_32bitLE && Melder_debug != 18) {
1011 			int32 l;
1012 			if (fread (& l, sizeof (int32), 1, f) != 1) readError (f, U"a signed 32-bit integer.");
1013 			return l;
1014 		} else {
1015 			uint8 bytes [4];
1016 			if (fread (bytes, sizeof (uint8), 4, f) != 4) readError (f, U"four bytes.");
1017 			return (int32)   // reinterpret sign bit
1018 				((uint32) ((uint32) bytes [3] << 24) |
1019 				 (uint32) ((uint32) bytes [2] << 16) |
1020 				 (uint32) ((uint32) bytes [1] << 8) |
1021 						   (uint32) bytes [0]);
1022 		}
1023 	} catch (MelderError) {
1024 		Melder_throw (U"Signed integer not read from 4 bytes in binary file.");
1025 	}
1026 }
1027 
bingetinteger32BE(FILE * f)1028 integer bingetinteger32BE (FILE *f) {
1029 	try {
1030 		if (binario_32bitBE && Melder_debug != 18) {
1031 			int32 l;
1032 			if (fread (& l, sizeof (int32), 1, f) != 1) readError (f, U"a signed 32-bit integer.");
1033 			return (integer) l;
1034 		} else {
1035 			uint8 bytes [4];
1036 			if (fread (bytes, sizeof (uint8), 4, f) != 4) readError (f, U"four bytes.");
1037 			return (integer) (int32)
1038 				((uint32) ((uint32) bytes [0] << 24) |
1039 				 (uint32) ((uint32) bytes [1] << 16) |
1040 				 (uint32) ((uint32) bytes [2] << 8) |
1041 						   (uint32) bytes [3]);
1042 		}
1043 	} catch (MelderError) {
1044 		Melder_throw (U"Signed integer not read from 4 bytes in binary file.");
1045 	}
1046 }
1047 
bingetu32(FILE * f)1048 uint32 bingetu32 (FILE *f) {
1049 	try {
1050 		if (binario_32bitBE && Melder_debug != 18) {
1051 			uint32 l;
1052 			if (fread (& l, sizeof (uint32), 1, f) != 1) readError (f, U"an unsigned 32-bit integer.");
1053 			return l;
1054 		} else {
1055 			uint8 bytes [4];
1056 			if (fread (bytes, sizeof (uint8), 4, f) != 4) readError (f, U"four bytes.");
1057 			return
1058 				(uint32) ((uint32) bytes [0] << 24) |
1059 				(uint32) ((uint32) bytes [1] << 16) |
1060 				(uint32) ((uint32) bytes [2] << 8) |
1061 						  (uint32) bytes [3];
1062 		}
1063 	} catch (MelderError) {
1064 		Melder_throw (U"Unsigned integer not read from 4 bytes in binary file.");
1065 	}
1066 }
1067 
bingetu32LE(FILE * f)1068 uint32 bingetu32LE (FILE *f) {
1069 	try {
1070 		if (binario_32bitLE && Melder_debug != 18) {
1071 			uint32 l;
1072 			if (fread (& l, sizeof (uint32), 1, f) != 1) readError (f, U"an unsigned 32-bit integer.");
1073 			return l;
1074 		} else {
1075 			uint8 bytes [4];
1076 			if (fread (bytes, sizeof (uint8), 4, f) != 4) readError (f, U"four bytes.");
1077 			return
1078 				(uint32) ((uint32) bytes [3] << 24) |
1079 				(uint32) ((uint32) bytes [2] << 16) |
1080 				(uint32) ((uint32) bytes [1] << 8) |
1081 						  (uint32) bytes [0];
1082 		}
1083 	} catch (MelderError) {
1084 		Melder_throw (U"Unsigned integer not read from 4 bytes in binary file.");
1085 	}
1086 }
1087 
bingetr32(FILE * f)1088 double bingetr32 (FILE *f) {
1089 	try {
1090 		if (binario_floatIEEE4msb && Melder_debug != 18) {
1091 			float x;
1092 			if (fread (& x, sizeof (float), 1, f) != 1) readError (f, U"a 32-bit floating-point number.");
1093 			return x;
1094 		} else {
1095 			uint8 bytes [4];
1096 			if (fread (bytes, sizeof (uint8), 4, f) != 4) readError (f, U"four bytes.");
1097 			int32 exponent = (int32)
1098 				((uint32) ((uint32) ((uint32) bytes [0] & 0x0000'007F) << 1) |
1099 				 (uint32) ((uint32) ((uint32) bytes [1] & 0x0000'0080) >> 7));   // between 0 and 255 (it's signed because we're going to subtract something)
1100 			uint32 mantissa =
1101 				(uint32) ((uint32) ((uint32) bytes [1] & 0x0000'007F) << 16) |
1102 						  (uint32) ((uint32) bytes [2] << 8) |
1103 									(uint32) bytes [3];
1104 			double x;
1105 			if (exponent == 0)
1106 				if (mantissa == 0) x = 0.0;
1107 				else x = ldexp ((double) mantissa, exponent - 149);   // denormalized
1108 			else if (exponent == 0x0000'00FF)   // Infinity or Not-a-Number
1109 				return undefined;
1110 			else   // finite
1111 				x = ldexp ((double) (mantissa | 0x0080'0000), exponent - 150);
1112 			return bytes [0] & 0x80 ? - x : x;
1113 		}
1114 	} catch (MelderError) {
1115 		Melder_throw (U"Floating-point number not read from 4 bytes in binary file.");
1116 	}
1117 }
1118 
bingetr32LE(FILE * f)1119 double bingetr32LE (FILE *f) {
1120 	try {
1121 		if (binario_floatIEEE4lsb && Melder_debug != 18) {
1122 			float x;
1123 			if (fread (& x, sizeof (float), 1, f) != 1) readError (f, U"a 32-bit floating-point number.");
1124 			return x;
1125 		} else {
1126 			uint8 bytes [4];
1127 			if (fread (bytes, sizeof (uint8), 4, f) != 4) readError (f, U"four bytes.");
1128 			int32 exponent = (int32)
1129 				((uint32) ((uint32) ((uint32) bytes [3] & 0x0000'007F) << 1) |
1130 				 (uint32) ((uint32) ((uint32) bytes [2] & 0x0000'0080) >> 7));
1131 			uint32 mantissa =
1132 				(uint32) ((uint32) ((uint32) bytes [2] & 0x0000'007F) << 16) |
1133 						  (uint32) ((uint32) bytes [1] << 8) |
1134 									(uint32) bytes [0];
1135 			double x;
1136 			if (exponent == 0)
1137 				if (mantissa == 0) x = 0.0;
1138 				else x = ldexp ((double) mantissa, exponent - 149);   // denormalized
1139 			else if (exponent == 0x0000'00FF)   // Infinity or Not-a-Number
1140 				return undefined;
1141 			else   // finite
1142 				x = ldexp ((double) (mantissa | 0x0080'0000), exponent - 150);
1143 			return bytes [3] & 0x80 ? - x : x;
1144 		}
1145 	} catch (MelderError) {
1146 		Melder_throw (U"Floating-point number not read from 4 bytes in binary file.");
1147 	}
1148 }
1149 
bingetr64(FILE * f)1150 double bingetr64 (FILE *f) {
1151 	try {
1152 		if (binario_doubleIEEE8msb && Melder_debug != 18 || Melder_debug == 181) {
1153 			double x;
1154 			if (fread (& x, sizeof (double), 1, f) != 1) readError (f, U"a 64-bit floating-point number.");
1155 			return x;
1156 		} else {
1157 			uint8 bytes [8];
1158 			if (fread (bytes, sizeof (uint8), 8, f) != 8) readError (f, U"eight bytes.");
1159 			int32 exponent = (int32)
1160 				((uint32) ((uint32) ((uint32) bytes [0] & 0x0000'007F) << 4) |
1161 				 (uint32) ((uint32) ((uint32) bytes [1] & 0x0000'00F0) >> 4));
1162 			uint32 highMantissa =
1163 				(uint32) ((uint32) ((uint32) bytes [1] & 0x0000'000F) << 16) |
1164 						  (uint32) ((uint32) bytes [2] << 8) |
1165 									(uint32) bytes [3];
1166 			uint32 lowMantissa =
1167 				(uint32) ((uint32) bytes [4] << 24) |
1168 				(uint32) ((uint32) bytes [5] << 16) |
1169 				(uint32) ((uint32) bytes [6] << 8) |
1170 						  (uint32) bytes [7];
1171 			double x;
1172 			if (exponent == 0)
1173 				if (highMantissa == 0 && lowMantissa == 0) x = 0.0;
1174 				else x = ldexp ((double) highMantissa, exponent - 1042) +
1175 					ldexp ((double) lowMantissa, exponent - 1074);   // denormalized
1176 			else if (exponent == 0x0000'07FF)   // Infinity or Not-a-Number
1177 				return undefined;
1178 			else
1179 				x = ldexp ((double) (highMantissa | 0x0010'0000), exponent - 1043) +
1180 					ldexp ((double) lowMantissa, exponent - 1075);
1181 			return bytes [0] & 0x80 ? - x : x;
1182 		}
1183 	} catch (MelderError) {
1184 		Melder_throw (U"Floating-point number not read from 8 bytes in binary file.");
1185 	}
1186 }
1187 
bingetr64LE(FILE * f)1188 double bingetr64LE (FILE *f) {
1189 	try {
1190 		if (binario_doubleIEEE8lsb && Melder_debug != 18 || Melder_debug == 181) {
1191 			double x;
1192 			if (fread (& x, sizeof (double), 1, f) != 1) readError (f, U"a 64-bit floating-point number.");
1193 			return x;
1194 		} else {
1195 			uint8 bytes [8];
1196 			if (fread (bytes, sizeof (uint8), 8, f) != 8) readError (f, U"eight bytes.");
1197 			int32 exponent = (int32)
1198 				((uint32) ((uint32) ((uint32) bytes [7] & 0x0000'007F) << 4) |
1199 				 (uint32) ((uint32) ((uint32) bytes [6] & 0x0000'00F0) >> 4));
1200 			uint32 highMantissa =
1201 				(uint32) ((uint32) ((uint32) bytes [6] & 0x0000'000F) << 16) |
1202 						  (uint32) ((uint32) bytes [5] << 8) |
1203 									(uint32) bytes [4];
1204 			uint32 lowMantissa =
1205 				(uint32) ((uint32) bytes [3] << 24) |
1206 				(uint32) ((uint32) bytes [2] << 16) |
1207 				(uint32) ((uint32) bytes [1] << 8) |
1208 						  (uint32) bytes [0];
1209 			double x;
1210 			if (exponent == 0)
1211 				if (highMantissa == 0 && lowMantissa == 0) x = 0.0;
1212 				else x = ldexp ((double) highMantissa, exponent - 1042) +
1213 					ldexp ((double) lowMantissa, exponent - 1074);   // denormalized
1214 			else if (exponent == 0x0000'07FF)   // Infinity or Not-a-Number
1215 				return undefined;
1216 			else
1217 				x = ldexp ((double) (highMantissa | 0x0010'0000), exponent - 1043) +
1218 					ldexp ((double) lowMantissa, exponent - 1075);
1219 			return bytes [7] & 0x80 ? - x : x;
1220 		}
1221 	} catch (MelderError) {
1222 		Melder_throw (U"Floating-point number not read from 8 bytes in binary file.");
1223 	}
1224 }
1225 
bingetr80(FILE * f)1226 double bingetr80 (FILE *f) {
1227 	try {
1228 		uint8 bytes [10];
1229 		if (fread (bytes, sizeof (uint8), 10, f) != 10) readError (f, U"ten bytes.");
1230 		int32 exponent = (int32)
1231 			((uint32) ((uint32) ((uint32) bytes [0] & 0x0000'007F) << 8) |
1232 								 (uint32) bytes [1]);   // between 0 and 32767
1233 		uint32 highMantissa =
1234 			(uint32) ((uint32) bytes [2] << 24) |
1235 			(uint32) ((uint32) bytes [3] << 16) |
1236 			(uint32) ((uint32) bytes [4] << 8) |
1237 					  (uint32) bytes [5];
1238 		uint32 lowMantissa =
1239 			(uint32) ((uint32) bytes [6] << 24) |
1240 			(uint32) ((uint32) bytes [7] << 16) |
1241 			(uint32) ((uint32) bytes [8] << 8) |
1242 					  (uint32) bytes [9];
1243 		double x;
1244 		if (exponent == 0 && highMantissa == 0 && lowMantissa == 0) x = 0.0;
1245 		else if (exponent == 0x0000'7FFF) return undefined;   // Infinity or NaN
1246 		else {
1247 			exponent -= 16'383;   // between -16'382 and +16'383
1248 			x = ldexp ((double) highMantissa, exponent - 31);
1249 			x += ldexp ((double) lowMantissa, exponent - 63);
1250 		}
1251 		return bytes [0] & 0x80 ? - x : x;
1252 	} catch (MelderError) {
1253 		Melder_throw (U"Floating-point number not read from 10 bytes in binary file.");
1254 	}
1255 }
1256 
1257 static int bitsInWriteBuffer = 0;
1258 static unsigned char writeBuffer = 0;
1259 
1260 #define macro_binputb(nbits) \
1261 void binputb##nbits (unsigned int value, FILE *f) { \
1262 	if (bitsInWriteBuffer + nbits > 8) { \
1263 		if (fputc (writeBuffer, f) < 0) writeError (U"a bit."); \
1264 		bitsInWriteBuffer = 0; \
1265 		writeBuffer = 0; \
1266 	} \
1267 	writeBuffer |= (value << (8 - nbits)) >> bitsInWriteBuffer; \
1268 	bitsInWriteBuffer += nbits; \
1269 }
1270 macro_binputb (1)
1271 macro_binputb (2)
1272 macro_binputb (3)
1273 macro_binputb (4)
1274 macro_binputb (5)
1275 macro_binputb (6)
1276 macro_binputb (7)
binputb(FILE * f)1277 void binputb (FILE *f) {
1278 	if (bitsInWriteBuffer == 0) return;
1279 	if (fputc (writeBuffer, f) < 0) writeError (U"a bit.");   // flush
1280 	bitsInWriteBuffer = 0;
1281 	writeBuffer = 0;
1282 }
1283 
binputi16(int16 i,FILE * f)1284 void binputi16 (int16 i, FILE *f) {
1285 	try {
1286 		if (binario_16bitBE && Melder_debug != 18) {
1287 			if (fwrite (& i, sizeof (short), 1, f) != 1) writeError (U"a signed 16-bit integer.");
1288 		} else {
1289 			uint8 bytes [2];
1290 			bytes [0] = (uint8) (i >> 8);   // truncate
1291 			bytes [1] = (uint8) i;   // truncate
1292 			if (fwrite (bytes, sizeof (uint8), 2, f) != 2) writeError (U"two bytes.");
1293 		}
1294 	} catch (MelderError) {
1295 		Melder_throw (U"Signed integer not written to 2 bytes in binary file.");
1296 	}
1297 }
1298 
binputi16LE(int16 i,FILE * f)1299 void binputi16LE (int16 i, FILE *f) {
1300 	try {
1301 		if (binario_16bitLE && Melder_debug != 18) {
1302 			if (fwrite (& i, sizeof (short), 1, f) != 1) writeError (U"a signed 16-bit integer.");
1303 		} else {
1304 			uint8 bytes [2];
1305 			bytes [1] = (uint8) (i >> 8);   // truncate
1306 			bytes [0] = (uint8) i;   // truncate
1307 			if (fwrite (bytes, sizeof (uint8), 2, f) != 2) writeError (U"two bytes.");
1308 		}
1309 	} catch (MelderError) {
1310 		Melder_throw (U"Signed integer not written to 2 bytes in binary file.");
1311 	}
1312 }
1313 
binputinteger16BE(integer i,FILE * f)1314 void binputinteger16BE (integer i, FILE *f) {
1315 	try {
1316 		if (i < INT16_MIN || i > INT16_MAX)
1317 			Melder_throw (U"The number ", i, U" is too big to fit into 16 bits.");   // this will change in the future
1318 		uint8 bytes [2];
1319 		bytes [0] = (uint8) (i >> 8);   // truncate
1320 		bytes [1] = (uint8) i;   // truncate
1321 		if (fwrite (bytes, sizeof (uint8), 2, f) != 2) writeError (U"two bytes.");
1322 	} catch (MelderError) {
1323 		Melder_throw (U"Signed integer not written to 2 bytes in binary file.");
1324 	}
1325 }
1326 
binputu16(uint16 u,FILE * f)1327 void binputu16 (uint16 u, FILE *f) {
1328 	try {
1329 		if (binario_16bitBE && Melder_debug != 18) {
1330 			if (fwrite (& u, sizeof (uint16), 1, f) != 1) writeError (U"an unsigned 16-bit integer.");
1331 		} else {
1332 			uint8 bytes [2];
1333 			bytes [0] = (uint8) (u >> 8);   // truncate
1334 			bytes [1] = (uint8) u;   // truncate
1335 			if (fwrite (bytes, sizeof (uint8), 2, f) != 2) writeError (U"two bytes.");
1336 		}
1337 	} catch (MelderError) {
1338 		Melder_throw (U"Unsigned integer not written to 2 bytes in binary file.");
1339 	}
1340 }
1341 
binputu16LE(uint16 u,FILE * f)1342 void binputu16LE (uint16 u, FILE *f) {
1343 	try {
1344 		if (binario_16bitLE && Melder_debug != 18) {
1345 			if (fwrite (& u, sizeof (uint16), 1, f) != 1) writeError (U"an unsigned 16-bit integer.");
1346 		} else {
1347 			uint8 bytes [2];
1348 			bytes [1] = (uint8) (u >> 8);   // truncate
1349 			bytes [0] = (uint8) u;   // truncate
1350 			if (fwrite (bytes, sizeof (uint8), 2, f) != 2) writeError (U"two bytes.");
1351 		}
1352 	} catch (MelderError) {
1353 		Melder_throw (U"Unsigned integer not written to 2 bytes in binary file.");
1354 	}
1355 }
1356 
binpute16(int value,FILE * f)1357 void binpute16 (int value, FILE *f) {
1358 	try {
1359 		if (binario_16bitBE && Melder_debug != 18) {
1360 			short s = value;
1361 			if (fwrite (& s, sizeof (short), 1, f) != 1) writeError (U"a signed 16-bit integer");
1362 		} else {
1363 			uint8 bytes [2];
1364 			bytes [0] = (uint8) (value >> 8);   // truncate
1365 			bytes [1] = (uint8) value;   // truncate
1366 			if (fwrite (bytes, sizeof (uint8), 2, f) != 2) writeError (U"two bytes.");
1367 		}
1368 	} catch (MelderError) {
1369 		Melder_throw (U"Enumerated value not written to 2 bytes in binary file.");
1370 	}
1371 }
1372 
binputi24(int32 i,FILE * f)1373 void binputi24 (int32 i, FILE *f) {
1374 	try {
1375 		uint8 bytes [3];
1376 		bytes [0] = (uint8) (i >> 16);   // truncate
1377 		bytes [1] = (uint8) (i >> 8);   // truncate
1378 		bytes [2] = (uint8) i;   // truncate
1379 		if (fwrite (bytes, sizeof (uint8), 3, f) != 3) writeError (U"three bytes");
1380 	} catch (MelderError) {
1381 		Melder_throw (U"Signed integer not written to 3 bytes in binary file.");
1382 	}
1383 }
1384 
binputi24LE(int32 i,FILE * f)1385 void binputi24LE (int32 i, FILE *f) {
1386 	try {
1387 		uint8 bytes [3];
1388 		bytes [2] = (uint8) (i >> 16);   // truncate
1389 		bytes [1] = (uint8) (i >> 8);   // truncate
1390 		bytes [0] = (uint8) i;   // truncate
1391 		if (fwrite (bytes, sizeof (uint8), 3, f) != 3) writeError (U"three bytes");
1392 	} catch (MelderError) {
1393 		Melder_throw (U"Signed integer not written to 3 bytes in binary file.");
1394 	}
1395 }
1396 
binputi32(int32 i,FILE * f)1397 void binputi32 (int32 i, FILE *f) {
1398 	try {
1399 		if (binario_32bitBE && Melder_debug != 18) {
1400 			if (fwrite (& i, sizeof (int32), 1, f) != 1) writeError (U"a signed 32-bit integer.");
1401 		} else {
1402 			uint8 bytes [4];
1403 			bytes [0] = (uint8) (i >> 24);   // truncate
1404 			bytes [1] = (uint8) (i >> 16);   // truncate
1405 			bytes [2] = (uint8) (i >> 8);   // truncate
1406 			bytes [3] = (uint8) i;   // truncate
1407 			if (fwrite (bytes, sizeof (uint8), 4, f) != 4) writeError (U"four bytes.");
1408 		}
1409 	} catch (MelderError) {
1410 		Melder_throw (U"Signed integer not written to 4 bytes in binary file.");
1411 	}
1412 }
1413 
binputi32LE(int32 i,FILE * f)1414 void binputi32LE (int32 i, FILE *f) {
1415 	try {
1416 		if (binario_32bitLE && Melder_debug != 18) {
1417 			if (fwrite (& i, sizeof (int32), 1, f) != 1) writeError (U"a signed 32-bit integer.");
1418 		} else {
1419 			uint8 bytes [4];
1420 			bytes [3] = (uint8) (i >> 24);   // truncate
1421 			bytes [2] = (uint8) (i >> 16);   // truncate
1422 			bytes [1] = (uint8) (i >> 8);   // truncate
1423 			bytes [0] = (uint8) i;   // truncate
1424 			if (fwrite (bytes, sizeof (uint8), 4, f) != 4) writeError (U"four bytes.");
1425 		}
1426 	} catch (MelderError) {
1427 		Melder_throw (U"Signed integer not written to 4 bytes in binary file.");
1428 	}
1429 }
1430 
binputinteger32BE(integer i,FILE * f)1431 void binputinteger32BE (integer i, FILE *f) {
1432 	try {
1433 		if (i < INT32_MIN || i > INT32_MAX)
1434 			Melder_throw (U"The number ", i, U" is too big to fit into 32 bits.");   // this will change in the future
1435 		uint8 bytes [4];
1436 		bytes [0] = (uint8) (i >> 24);   // truncate
1437 		bytes [1] = (uint8) (i >> 16);   // truncate
1438 		bytes [2] = (uint8) (i >> 8);   // truncate
1439 		bytes [3] = (uint8) i;   // truncate
1440 		if (fwrite (bytes, sizeof (uint8), 4, f) != 4) writeError (U"a signed 32-bit integer.");
1441 	} catch (MelderError) {
1442 		Melder_throw (U"Signed integer not written to 4 bytes in binary file.");
1443 	}
1444 }
1445 
binputu32(uint32 u,FILE * f)1446 void binputu32 (uint32 u, FILE *f) {
1447 	try {
1448 		if (binario_32bitBE && Melder_debug != 18) {
1449 			if (fwrite (& u, sizeof (uint32), 1, f) != 1) writeError (U"an unsigned 32-bit integer.");
1450 		} else {
1451 			uint8 bytes [4];
1452 			bytes [0] = (uint8) (u >> 24);   // truncate
1453 			bytes [1] = (uint8) (u >> 16);   // truncate
1454 			bytes [2] = (uint8) (u >> 8);   // truncate
1455 			bytes [3] = (uint8) u;   // truncate
1456 			if (fwrite (bytes, sizeof (uint8), 4, f) != 4) writeError (U"four bytes.");
1457 		}
1458 	} catch (MelderError) {
1459 		Melder_throw (U"Unsigned integer not written to 4 bytes in binary file.");
1460 	}
1461 }
1462 
binputu32LE(uint32 u,FILE * f)1463 void binputu32LE (uint32 u, FILE *f) {
1464 	try {
1465 		if (binario_32bitLE && Melder_debug != 18) {
1466 			if (fwrite (& u, sizeof (uint32), 1, f) != 1) writeError (U"an unsigned 32-bit integer.");
1467 		} else {
1468 			uint8 bytes [4];
1469 			bytes [3] = (uint8) (u >> 24);   // truncate
1470 			bytes [2] = (uint8) (u >> 16);  // truncate
1471 			bytes [1] = (uint8) (u >> 8);  // truncate
1472 			bytes [0] = (uint8) u;  // truncate
1473 			if (fwrite (bytes, sizeof (uint8), 4, f) != 4) writeError (U"four bytes.");
1474 		}
1475 	} catch (MelderError) {
1476 		Melder_throw (U"Unsigned integer not written to 4 bytes in binary file.");
1477 	}
1478 }
1479 
binputr32(double x,FILE * f)1480 void binputr32 (double x, FILE *f) {
1481 	try {
1482 		if (binario_floatIEEE4msb && Melder_debug != 18) {
1483 			float x32 = (float) x;   // convert down, with loss of precision
1484 			if (fwrite (& x32, sizeof (float), 1, f) != 1) writeError (U"a 32-bit floating-point number.");
1485 		} else {
1486 			uint8 bytes [4];
1487 			int sign, exponent;
1488 			double fMantissa, fsMantissa;
1489 			uint32 mantissa;
1490 			if (x < 0.0) { sign = 0x0100; x *= -1.0; }
1491 			else sign = 0;
1492 			if (x == 0.0) { exponent = 0; mantissa = 0; }
1493 			else {
1494 				fMantissa = frexp (x, & exponent);
1495 				if ((exponent > 128) || ! (fMantissa < 1.0))   // Infinity or Not-a-Number
1496 					{ exponent = sign | 0x00FF; mantissa = 0; }   // Infinity
1497 				else {   // finite
1498 					exponent += 126;   // add bias
1499 					if (exponent <= 0) {   // denormalized
1500 						fMantissa = ldexp (fMantissa, exponent - 1);
1501 						exponent = 0;
1502 					}
1503 					exponent |= sign;
1504 					fMantissa = ldexp (fMantissa, 24);
1505 					fsMantissa = floor (fMantissa);
1506 					mantissa = (uint32) fsMantissa & 0x007FFFFF;
1507 				}
1508 			}
1509 			bytes [0] = (uint8) (exponent >> 1);   // truncate: bits 2 through 9 (bit 9 is the sign bit)
1510 			bytes [1] = (uint8) ((exponent << 7) | (mantissa >> 16));   // truncate
1511 			bytes [2] = (uint8) (mantissa >> 8);   // truncate
1512 			bytes [3] = (uint8) mantissa;   // truncate
1513 			if (fwrite (bytes, sizeof (uint8), 4, f) != 4) writeError (U"four bytes.");
1514 		}
1515 	} catch (MelderError) {
1516 		Melder_throw (U"Floating-point number not written to 4 bytes in binary file.");
1517 	}
1518 }
1519 
binputr32LE(double x,FILE * f)1520 void binputr32LE (double x, FILE *f) {
1521 	try {
1522 		if (binario_floatIEEE4lsb && Melder_debug != 18) {
1523 			float x32 = (float) x;   // convert down, with loss of precision
1524 			if (fwrite (& x32, sizeof (float), 1, f) != 1) writeError (U"a 32-bit floating-point number.");
1525 		} else {
1526 			uint8 bytes [4];
1527 			int sign, exponent;
1528 			double fMantissa, fsMantissa;
1529 			uint32 mantissa;
1530 			if (x < 0.0) { sign = 0x0100; x *= -1.0; }
1531 			else sign = 0;
1532 			if (x == 0.0) { exponent = 0; mantissa = 0; }
1533 			else {
1534 				fMantissa = frexp (x, & exponent);
1535 				if ((exponent > 128) || ! (fMantissa < 1.0))   // Infinity or Not-a-Number
1536 					{ exponent = sign | 0x00FF; mantissa = 0; }   // Infinity
1537 				else {   // finite
1538 					exponent += 126;   // add bias
1539 					if (exponent <= 0) {   // denormalized
1540 						fMantissa = ldexp (fMantissa, exponent - 1);
1541 						exponent = 0;
1542 					}
1543 					exponent |= sign;
1544 					fMantissa = ldexp (fMantissa, 24);
1545 					fsMantissa = floor (fMantissa);
1546 					mantissa = (uint32) fsMantissa & 0x007F'FFFF;
1547 				}
1548 			}
1549 			bytes [3] = (uint8) (exponent >> 1);
1550 			bytes [2] = (uint8) ((exponent << 7) | (mantissa >> 16));
1551 			bytes [1] = (uint8) (mantissa >> 8);
1552 			bytes [0] = (uint8) mantissa;
1553 			if (fwrite (bytes, sizeof (uint8), 4, f) != 4) writeError (U"four bytes.");
1554 		}
1555 	} catch (MelderError) {
1556 		Melder_throw (U"Floating-point number not written to 4 bytes in binary file.");
1557 	}
1558 }
1559 
binputr64(double x,FILE * f)1560 void binputr64 (double x, FILE *f) {
1561 	try {
1562 		if (binario_doubleIEEE8msb && Melder_debug != 18 || Melder_debug == 181) {
1563 			if (fwrite (& x, sizeof (double), 1, f) != 1) writeError (U"a 64-bit floating-point number.");
1564 		} else if (binario_doubleIEEE8lsb && Melder_debug != 18) {
1565 			union { double xx; uint8 bytes [8]; };
1566 			xx = x;
1567 			std::swap (bytes [0], bytes [7]);
1568 			std::swap (bytes [1], bytes [6]);
1569 			std::swap (bytes [2], bytes [5]);
1570 			std::swap (bytes [3], bytes [4]);
1571 			if (fwrite (& xx, sizeof (double), 1, f) != 1) writeError (U"a 64-bit floating-point number.");
1572 		} else {
1573 			uint8 bytes [8];
1574 			int sign, exponent;
1575 			double fMantissa, fsMantissa;
1576 			uint32 highMantissa, lowMantissa;
1577 			if (x < 0.0) { sign = 0x0800; x *= -1.0; }
1578 			else sign = 0;
1579 			if (x == 0.0) { exponent = 0; highMantissa = 0; lowMantissa = 0; }
1580 			else {
1581 				fMantissa = frexp (x, & exponent);
1582 				if (/*(exponent > 1024) ||*/ ! (fMantissa < 1.0))   // Infinity or Not-a-Number
1583 					{ exponent = sign | 0x07FF; highMantissa = 0; lowMantissa = 0; }   // Infinity
1584 				else { // finite
1585 					exponent += 1022;   // add bias
1586 					if (exponent <= 0) {   // denormalized
1587 						fMantissa = ldexp (fMantissa, exponent - 1);
1588 						exponent = 0;
1589 					}
1590 					exponent |= sign;
1591 					fMantissa = ldexp (fMantissa, 21);
1592 					fsMantissa = floor (fMantissa);
1593 					highMantissa = (uint32) fsMantissa & 0x000F'FFFF;
1594 					fMantissa = ldexp (fMantissa - fsMantissa, 32);
1595 					fsMantissa = floor (fMantissa);
1596 					lowMantissa = (uint32) fsMantissa;
1597 				}
1598 			}
1599 			bytes [0] = (uint8) (exponent >> 4);
1600 			bytes [1] = (uint8) ((exponent << 4) | (highMantissa >> 16));
1601 			bytes [2] = (uint8) (highMantissa >> 8);
1602 			bytes [3] = (uint8) highMantissa;
1603 			bytes [4] = (uint8) (lowMantissa >> 24);
1604 			bytes [5] = (uint8) (lowMantissa >> 16);
1605 			bytes [6] = (uint8) (lowMantissa >> 8);
1606 			bytes [7] = (uint8) lowMantissa;
1607 			if (fwrite (bytes, sizeof (uint8), 8, f) != 8) writeError (U"eight bytes.");
1608 		}
1609 	} catch (MelderError) {
1610 		Melder_throw (U"Floating-point number not written to 8 bytes in binary file.");
1611 	}
1612 }
1613 
binputr64LE(double x,FILE * f)1614 void binputr64LE (double x, FILE *f) {
1615 	try {
1616 		if (binario_doubleIEEE8lsb && Melder_debug != 18 || Melder_debug == 181) {
1617 			if (fwrite (& x, sizeof (double), 1, f) != 1) writeError (U"a 64-bit floating-point number.");
1618 		} else if (binario_doubleIEEE8msb && Melder_debug != 18) {
1619 			union { double xx; uint8 bytes [8]; };
1620 			xx = x;
1621 			std::swap (bytes [0], bytes [7]);
1622 			std::swap (bytes [1], bytes [6]);
1623 			std::swap (bytes [2], bytes [5]);
1624 			std::swap (bytes [3], bytes [4]);
1625 			if (fwrite (& xx, sizeof (double), 1, f) != 1) writeError (U"a 64-bit floating-point number.");
1626 		} else {
1627 			uint8 bytes [8];
1628 			int sign, exponent;
1629 			double fMantissa, fsMantissa;
1630 			uint32 highMantissa, lowMantissa;
1631 			if (x < 0.0) { sign = 0x0800; x *= -1.0; }
1632 			else sign = 0;
1633 			if (x == 0.0) { exponent = 0; highMantissa = 0; lowMantissa = 0; }
1634 			else {
1635 				fMantissa = frexp (x, & exponent);
1636 				if (/*(exponent > 1024) ||*/ ! (fMantissa < 1.0))   // Infinity or Not-a-Number
1637 					{ exponent = sign | 0x07FF; highMantissa = 0; lowMantissa = 0; }   // Infinity
1638 				else { // finite
1639 					exponent += 1022;   // add bias
1640 					if (exponent <= 0) {   // denormalized
1641 						fMantissa = ldexp (fMantissa, exponent - 1);
1642 						exponent = 0;
1643 					}
1644 					exponent |= sign;
1645 					fMantissa = ldexp (fMantissa, 21);
1646 					fsMantissa = floor (fMantissa);
1647 					highMantissa = (uint32) fsMantissa & 0x000F'FFFF;
1648 					fMantissa = ldexp (fMantissa - fsMantissa, 32);
1649 					fsMantissa = floor (fMantissa);
1650 					lowMantissa = (uint32) fsMantissa;
1651 				}
1652 			}
1653 			bytes [7] = (uint8) (exponent >> 4);
1654 			bytes [6] = (uint8) ((exponent << 4) | (highMantissa >> 16));
1655 			bytes [5] = (uint8) (highMantissa >> 8);
1656 			bytes [4] = (uint8) highMantissa;
1657 			bytes [3] = (uint8) (lowMantissa >> 24);
1658 			bytes [2] = (uint8) (lowMantissa >> 16);
1659 			bytes [1] = (uint8) (lowMantissa >> 8);
1660 			bytes [0] = (uint8) lowMantissa;
1661 			if (fwrite (bytes, sizeof (uint8), 8, f) != 8) writeError (U"eight bytes.");
1662 		}
1663 	} catch (MelderError) {
1664 		Melder_throw (U"Floating-point number not written to 8 bytes in binary file.");
1665 	}
1666 }
1667 
binputr80(double x,FILE * f)1668 void binputr80 (double x, FILE *f) {
1669 	try {
1670 		unsigned char bytes [10];
1671 		Melder_assert (sizeof (int) > 2);
1672 		int sign, exponent;   // these should be uint16, but frexp() expects an int
1673 		double fMantissa, fsMantissa;
1674 		uint32 highMantissa, lowMantissa;
1675 		if (x < 0.0) { sign = 0x8000; x *= -1.0; }
1676 		else sign = 0;
1677 		if (x == 0.0) { exponent = 0; highMantissa = 0; lowMantissa = 0; }
1678 		else {
1679 			fMantissa = frexp (x, & exponent);
1680 			if ((exponent > 16384) || ! (fMantissa < 1.0))   // Infinity or Not-a-Number
1681 				{ exponent = sign | 0x7FFF; highMantissa = 0; lowMantissa = 0; }   // Infinity
1682 			else {   // finite
1683 				exponent += 16382;   // add bias
1684 				if (exponent < 0) {   // denormalized
1685 					fMantissa = ldexp (fMantissa, exponent);
1686 					exponent = 0;
1687 				}
1688 				exponent |= sign;
1689 				fMantissa = ldexp (fMantissa, 32);
1690 				fsMantissa = floor (fMantissa);
1691 				highMantissa = (uint32) fsMantissa;
1692 				fMantissa = ldexp (fMantissa - fsMantissa, 32);
1693 				fsMantissa = floor (fMantissa);
1694 				lowMantissa = (uint32) fsMantissa;
1695 			}
1696 		}
1697 		bytes [0] = (uint8) (exponent >> 8);
1698 		bytes [1] = (uint8) exponent;
1699 		bytes [2] = (uint8) (highMantissa >> 24);
1700 		bytes [3] = (uint8) (highMantissa >> 16);
1701 		bytes [4] = (uint8) (highMantissa >> 8);
1702 		bytes [5] = (uint8) highMantissa;
1703 		bytes [6] = (uint8) (lowMantissa >> 24);
1704 		bytes [7] = (uint8) (lowMantissa >> 16);
1705 		bytes [8] = (uint8) (lowMantissa >> 8);
1706 		bytes [9] = (uint8) lowMantissa;
1707 		if (fwrite (bytes, sizeof (uint8), 10, f) != 10) writeError (U"ten bytes.");
1708 	} catch (MelderError) {
1709 		Melder_throw (U"Floating-point number not written to 10 bytes in binary file.");
1710 	}
1711 }
1712 
bingetc64(FILE * f)1713 dcomplex bingetc64 (FILE *f) {
1714 	try {
1715 		dcomplex result;
1716 		result. real (bingetr32 (f));
1717 		result. imag (bingetr32 (f));
1718 		return result;
1719 	} catch (MelderError) {
1720 		Melder_throw (U"Complex number not read from 8 bytes in binary file.");
1721 		dcomplex result { };
1722 		return result;
1723 	}
1724 }
1725 
bingetc128(FILE * f)1726 dcomplex bingetc128 (FILE *f) {
1727 	try {
1728 		dcomplex result;
1729 		result. real (bingetr64 (f));
1730 		result. imag (bingetr64 (f));
1731 		return result;
1732 	} catch (MelderError) {
1733 		Melder_throw (U"Complex number not read from 16 bytes in binary file.");
1734 		dcomplex result { };
1735 		return result;
1736 	}
1737 }
1738 
binputc64(dcomplex z,FILE * f)1739 void binputc64 (dcomplex z, FILE *f) {
1740 	try {
1741 		binputr32 (z.real(), f);
1742 		binputr32 (z.imag(), f);
1743 	} catch (MelderError) {
1744 		Melder_throw (U"Complex number not written to 8 bytes in binary file.");
1745 	}
1746 }
1747 
binputc128(dcomplex z,FILE * f)1748 void binputc128 (dcomplex z, FILE *f) {
1749 	try {
1750 		binputr64 (z.real(), f);
1751 		binputr64 (z.imag(), f);
1752 	} catch (MelderError) {
1753 		Melder_throw (U"Complex number not written to 16 bytes in binary file.");
1754 	}
1755 }
1756 
bingets8(FILE * f)1757 autostring8 bingets8 (FILE *f) {
1758 	try {
1759 		unsigned int length = bingetu8 (f);
1760 		autostring8 result (length);
1761 		if (fread (result.get(), sizeof (char), length, f) != length)
1762 			Melder_throw (feof (f) ? U"Reached end of file" : U"Error in file", U" while trying to read ", length, U" one-byte characters.");
1763 		result [length] = 0;   // trailing null byte
1764 		return result;
1765 	} catch (MelderError) {
1766 		Melder_throw (U"Text not read from a binary file.");
1767 	}
1768 }
1769 
bingets16(FILE * f)1770 autostring8 bingets16 (FILE *f) {
1771 	try {
1772 		uint16 length = bingetu16 (f);
1773 		autostring8 result (length);
1774 		if (fread (result.get(), sizeof (char), length, f) != length)
1775 			Melder_throw (feof (f) ? U"Reached end of file" : U"Error in file", U" while trying to read ", length, U" one-byte characters.");
1776 		result [length] = 0;   // trailing null byte
1777 		return result;
1778 	} catch (MelderError) {
1779 		Melder_throw (U"Text not read from a binary file.");
1780 	}
1781 }
1782 
bingets32(FILE * f)1783 autostring8 bingets32 (FILE *f) {
1784 	try {
1785 		uint32 length = bingetu32 (f);
1786 		autostring8 result (length);
1787 		if (fread (result.get(), sizeof (char), length, f) != length)
1788 			Melder_throw (feof (f) ? U"Reached end of file" : U"Error in file", U" while trying to read ", length, U" one-byte characters.");
1789 		result [length] = 0;   // trailing null byte
1790 		return result;
1791 	} catch (MelderError) {
1792 		Melder_throw (U"Text not read from a binary file.");
1793 	}
1794 }
1795 
bingetw8(FILE * f)1796 autostring32 bingetw8 (FILE *f) {
1797 	try {
1798 		autostring32 result;
1799 		unsigned int length = bingetu8 (f);
1800 		if (length == 0xFF) {   // an escape for encoding
1801 			/*
1802 				UTF-16
1803 			*/
1804 			length = bingetu8 (f);
1805 			result = autostring32 (length);
1806 			for (unsigned int i = 0; i < length; i ++) {
1807 				char32 kar = bingetu16 (f);
1808 				if ((kar & 0x00'F800) == 0x00'D800) {
1809 					if (kar > 0x00'DBFF)
1810 						Melder_throw (U"Incorrect Unicode value (first surrogate member ", kar, U").");
1811 					char32 kar2 = bingetu16 (f);
1812 					if (kar2 < 0x00'DC'00 || kar2 > 0x00'DF'FF)
1813 						Melder_throw (U"Incorrect Unicode value (second surrogate member ", kar2, U").");
1814 					result [i] = (((kar & 0x00'03FF) << 10) | (kar2 & 0x00'03FF)) + 0x01'0000;
1815 				} else {
1816 					result [i] = kar;
1817 				}
1818 			}
1819 		} else {
1820 			/*
1821 				ASCII
1822 			*/
1823 			result = autostring32 (length);
1824 			for (unsigned int i = 0; i < length; i ++) {
1825 				result [i] = bingetu8 (f);
1826 			}
1827 		}
1828 		result [length] = U'\0';
1829 		return result;
1830 	} catch (MelderError) {
1831 		Melder_throw (U"Text not read from a binary file.");
1832 	}
1833 }
1834 
bingetw16(FILE * f)1835 autostring32 bingetw16 (FILE *f) {
1836 	try {
1837 		autostring32 result;
1838 		uint16 length = bingetu16 (f);
1839 		if (length == 0xFFFF) {   // an escape for encoding
1840 			/*
1841 				UTF-16
1842 			*/
1843 			length = bingetu16 (f);
1844 			result = autostring32 (length);
1845 			for (uint16 i = 0; i < length; i ++) {
1846 				char32 kar = (char32) (char16) bingetu16 (f);
1847 				if ((kar & 0x00'F800) == 0x00'D800) {
1848 					if (kar > 0x00'DBFF)
1849 						Melder_throw (U"Incorrect Unicode value (first surrogate member ", kar, U").");
1850 					char32 kar2 = (char32) (char16) bingetu16 (f);
1851 					if (kar2 < 0x00'DC00 || kar2 > 0x00'DFFF)
1852 						Melder_throw (U"Incorrect Unicode value (second surrogate member ", kar2, U").");
1853 					result [i] = (((kar & 0x00'03FF) << 10) | (kar2 & 0x00'03FF)) + 0x01'0000;
1854 				} else {
1855 					result [i] = kar;
1856 				}
1857 			}
1858 		} else {
1859 			/*
1860 				ASCII
1861 			*/
1862 			result = autostring32 (length);
1863 			for (unsigned short i = 0; i < length; i ++) {
1864 				result [i] = (char32) (char8) bingetu8 (f);
1865 			}
1866 		}
1867 		result [length] = U'\0';
1868 		return result;
1869 	} catch (MelderError) {
1870 		Melder_throw (U"Text not read from a binary file.");
1871 	}
1872 }
1873 
bingetw32(FILE * f)1874 autostring32 bingetw32 (FILE *f) {
1875 	try {
1876 		autostring32 result;
1877 		uint32 length = bingetu32 (f);
1878 		if (length == 0xFFFF'FFFF) {   // an escape for encoding
1879 			/*
1880 				UTF-16
1881 			*/
1882 			length = bingetu32 (f);
1883 			result = autostring32 (length);
1884 			for (uint32 i = 0; i < length; i ++) {
1885 				char32 kar = bingetu16 (f);
1886 				if ((kar & 0x00'F800) == 0x00'D800) {
1887 					if (kar > 0x00'DBFF)
1888 						Melder_throw (U"Incorrect Unicode value (first surrogate member ", kar, U").");
1889 					char32 kar2 = bingetu16 (f);
1890 					if (kar2 < 0x00'DC00 || kar2 > 0x00'DFFF)
1891 						Melder_throw (U"Incorrect Unicode value (second surrogate member ", kar2, U").");
1892 					result [i] = (((kar & 0x00'03FF) << 10) | (kar2 & 0x00'03FF)) + 0x01'0000;
1893 				} else {
1894 					result [i] = kar;
1895 				}
1896 			}
1897 		} else {
1898 			/*
1899 				ASCII
1900 			*/
1901 			result = autostring32 (length);
1902 			for (uint32 i = 0; i < length; i ++) {
1903 				result [i] = bingetu8 (f);
1904 			}
1905 		}
1906 		result [length] = U'\0';
1907 		return result;
1908 	} catch (MelderError) {
1909 		Melder_throw (U"Text not read from a binary file.");
1910 	}
1911 }
1912 
binputs8(const char * s,FILE * f)1913 void binputs8 (const char *s, FILE *f) {
1914 	try {
1915 		if (! s) {
1916 			binputu8 (0, f);
1917 		} else {
1918 			size_t length = strlen (s);
1919 			if (length > UINT8_MAX) {
1920 				Melder_warning (U"Text of ", length, U" characters truncated to 255 characters.");
1921 				length = UINT8_MAX;
1922 			}
1923 			binputu8 (length, f);
1924 			if (fwrite (s, sizeof (char), length, f) != length)
1925 				Melder_throw (U"Error in file while trying to write ", length, U" one-byte characters.");
1926 		}
1927 	} catch (MelderError) {
1928 		Melder_throw (U"Text not written to a binary file.");
1929 	}
1930 }
1931 
binputs16(const char * s,FILE * f)1932 void binputs16 (const char *s, FILE *f) {
1933 	try {
1934 		if (! s) {
1935 			binputu16 (0, f);
1936 		} else {
1937 			size_t length = strlen (s);
1938 			if (length > UINT16_MAX) {
1939 				Melder_warning (U"Text of ", length, U" characters truncated to 65535 characters.");
1940 				length = UINT16_MAX;
1941 			}
1942 			binputu16 ((uint16) length, f);   // safe conversion down
1943 			if (fwrite (s, sizeof (char), length, f) != length)
1944 				Melder_throw (U"Error in file while trying to write ", length, U" one-byte characters.");
1945 		}
1946 	} catch (MelderError) {
1947 		Melder_throw (U"Text not written to a binary file.");
1948 	}
1949 }
1950 
binputs32(const char * s,FILE * f)1951 void binputs32 (const char *s, FILE *f) {
1952 	try {
1953 		if (! s) {
1954 			binputu32 (0, f);
1955 		} else {
1956 			size_t length = strlen (s);
1957 			if (length > UINT32_MAX) {
1958 				Melder_warning (U"Text of ", length, U" characters truncated to 4,294,967,295 characters.");
1959 				length = UINT32_MAX;
1960 			}
1961 			binputu32 (length, f);
1962 			if (fwrite (s, sizeof (char), length, f) != length)
1963 				Melder_throw (U"Error in file while trying to write ", length, U" one-byte characters.");
1964 		}
1965 	} catch (MelderError) {
1966 		Melder_throw (U"Text not written to a binary file.");
1967 	}
1968 }
1969 
binpututf16(char32 kar,FILE * f)1970 static inline void binpututf16 (char32 kar, FILE *f) {
1971 	if (kar <= 0x00FFFF) {
1972 		binputu16 ((uint16) kar, f);   // truncate to lower 16 bits
1973 	} else if (kar <= 0x10'FFFF) {
1974 		kar -= 0x01'0000;
1975 		binputu16 ((uint16) (0x00'D800 | (kar >> 10)), f);
1976 		binputu16 ((uint16) (0x00'DC00 | (kar & 0x00'03FF)), f);
1977 	} else {
1978 		Melder_fatal (U"Impossible Unicode value.");
1979 	}
1980 }
1981 
binputw8(conststring32 s,FILE * f)1982 void binputw8 (conststring32 s, FILE *f) {
1983 	try {
1984 		if (! s) {
1985 			binputu8 (0, f);
1986 		} else {
1987 			uint32 length = str32len (s);
1988 			if (length > UINT8_MAX - 1) {
1989 				Melder_warning (U"Text of ", length, U" characters truncated to 254 characters.");
1990 				length = UINT8_MAX - 1;
1991 			}
1992 			if (Melder_isValidAscii (s)) {
1993 				/*
1994 				 * ASCII
1995 				 */
1996 				binputu8 (length, f);
1997 				for (size_t i = 0; i < length; i ++) {
1998 					binputu8 ((unsigned int) (char) s [i], f);
1999 				}
2000 			} else {
2001 				/*
2002 				 * UTF-16
2003 				 */
2004 				binputu8 (0xFF, f);   // an escape for multibyte encoding
2005 				binputu8 (length, f);
2006 				for (size_t i = 0; i < length; i ++) {
2007 					binpututf16 (s [i], f);
2008 				}
2009 			}
2010 		}
2011 	} catch (MelderError) {
2012 		Melder_throw (U"Text not written to a binary file.");
2013 	}
2014 }
2015 
binputw16(conststring32 s,FILE * f)2016 void binputw16 (conststring32 s, FILE *f) {
2017 	try {
2018 		if (! s) {
2019 			binputu16 (0, f);
2020 		} else {
2021 			int64 length = str32len (s);
2022 			if (length > UINT16_MAX - 1) {
2023 				Melder_warning (U"Text of ", length, U" characters truncated to 65534 characters.");
2024 				length = UINT16_MAX - 1;
2025 			}
2026 			if (Melder_isValidAscii (s)) {
2027 				/*
2028 				 * ASCII
2029 				 */
2030 				binputu16 ((uint16) length, f);
2031 				for (int64 i = 0; i < length; i ++) {
2032 					binputu8 ((unsigned int) (char8) s [i], f);
2033 				}
2034 			} else {
2035 				/*
2036 				 * UTF-16
2037 				 */
2038 				binputu16 (0xFFFF, f);   // an escape for multibyte encoding
2039 				binputu16 ((uint16) length, f);
2040 				for (int64 i = 0; i < length; i ++) {
2041 					binpututf16 (s [i], f);
2042 				}
2043 			}
2044 		}
2045 	} catch (MelderError) {
2046 		Melder_throw (U"Text not written to a binary file.");
2047 	}
2048 }
2049 
binputw32(conststring32 s,FILE * f)2050 void binputw32 (conststring32 s, FILE *f) {
2051 	try {
2052 		if (! s) {
2053 			binputu32 (0, f);
2054 		} else {
2055 			int64 length = str32len (s);
2056 			if (length > UINT32_MAX - 1) {
2057 				Melder_warning (U"Text of ", length, U" characters truncated to 4,294,967,294 characters.");
2058 				length = UINT32_MAX - 1;
2059 			}
2060 			if (Melder_isValidAscii (s)) {
2061 				/*
2062 				 * ASCII
2063 				 */
2064 				binputu32 ((uint32) length, f);
2065 				for (int64 i = 0; i < length; i ++) {
2066 					binputu8 ((unsigned int) (char) s [i], f);
2067 				}
2068 			} else {
2069 				/*
2070 				 * UTF-16
2071 				 */
2072 				binputu32 (0xFFFF'FFFF, f);   // an escape for multibyte encoding
2073 				binputu32 ((uint32) length, f);
2074 				for (int64 i = 0; i < length; i ++) {
2075 					binpututf16 (s [i], f);
2076 				}
2077 			}
2078 		}
2079 	} catch (MelderError) {
2080 		Melder_throw (U"Text not written to a binary file.");
2081 	}
2082 }
2083 
2084 /* End of file abcio.cpp */
2085