Line data Source code
1 : #![allow(missing_docs)]
2 :
3 : use std::io::{self, Read};
4 : use std::ops::Range;
5 : use std::{cmp, str};
6 :
7 : use byteorder::{BigEndian, ByteOrder, ReadBytesExt};
8 : use bytes::{Bytes, BytesMut};
9 : use fallible_iterator::FallibleIterator;
10 : use memchr::memchr;
11 :
12 : use crate::Oid;
13 :
14 : // top-level message tags
15 : const PARSE_COMPLETE_TAG: u8 = b'1';
16 : const BIND_COMPLETE_TAG: u8 = b'2';
17 : const CLOSE_COMPLETE_TAG: u8 = b'3';
18 : pub const NOTIFICATION_RESPONSE_TAG: u8 = b'A';
19 : const COPY_DONE_TAG: u8 = b'c';
20 : const COMMAND_COMPLETE_TAG: u8 = b'C';
21 : const COPY_DATA_TAG: u8 = b'd';
22 : const DATA_ROW_TAG: u8 = b'D';
23 : const ERROR_RESPONSE_TAG: u8 = b'E';
24 : const COPY_IN_RESPONSE_TAG: u8 = b'G';
25 : const COPY_OUT_RESPONSE_TAG: u8 = b'H';
26 : const COPY_BOTH_RESPONSE_TAG: u8 = b'W';
27 : const EMPTY_QUERY_RESPONSE_TAG: u8 = b'I';
28 : const BACKEND_KEY_DATA_TAG: u8 = b'K';
29 : pub const NO_DATA_TAG: u8 = b'n';
30 : pub const NOTICE_RESPONSE_TAG: u8 = b'N';
31 : const AUTHENTICATION_TAG: u8 = b'R';
32 : const PORTAL_SUSPENDED_TAG: u8 = b's';
33 : pub const PARAMETER_STATUS_TAG: u8 = b'S';
34 : const PARAMETER_DESCRIPTION_TAG: u8 = b't';
35 : const ROW_DESCRIPTION_TAG: u8 = b'T';
36 : pub const READY_FOR_QUERY_TAG: u8 = b'Z';
37 :
38 : #[derive(Debug, Copy, Clone)]
39 : pub struct Header {
40 : tag: u8,
41 : len: i32,
42 : }
43 :
44 : #[allow(clippy::len_without_is_empty)]
45 : impl Header {
46 : #[inline]
47 104 : pub fn parse(buf: &[u8]) -> io::Result<Option<Header>> {
48 104 : if buf.len() < 5 {
49 49 : return Ok(None);
50 0 : }
51 :
52 55 : let tag = buf[0];
53 55 : let len = BigEndian::read_i32(&buf[1..]);
54 :
55 55 : if len < 4 {
56 0 : return Err(io::Error::new(
57 0 : io::ErrorKind::InvalidData,
58 0 : "invalid message length: header length < 4",
59 0 : ));
60 0 : }
61 :
62 55 : Ok(Some(Header { tag, len }))
63 0 : }
64 :
65 : #[inline]
66 97 : pub fn tag(self) -> u8 {
67 97 : self.tag
68 0 : }
69 :
70 : #[inline]
71 55 : pub fn len(self) -> i32 {
72 55 : self.len
73 0 : }
74 : }
75 :
76 : /// An enum representing Postgres backend messages.
77 : pub enum Message {
78 : AuthenticationCleartextPassword,
79 : AuthenticationGss,
80 : AuthenticationKerberosV5,
81 : AuthenticationMd5Password,
82 : AuthenticationOk,
83 : AuthenticationScmCredential,
84 : AuthenticationSspi,
85 : AuthenticationGssContinue,
86 : AuthenticationSasl(AuthenticationSaslBody),
87 : AuthenticationSaslContinue(AuthenticationSaslContinueBody),
88 : AuthenticationSaslFinal(AuthenticationSaslFinalBody),
89 : BackendKeyData(BackendKeyDataBody),
90 : BindComplete,
91 : CloseComplete,
92 : CommandComplete(CommandCompleteBody),
93 : CopyData,
94 : CopyDone,
95 : CopyInResponse,
96 : CopyOutResponse,
97 : CopyBothResponse,
98 : DataRow(DataRowBody),
99 : EmptyQueryResponse,
100 : ErrorResponse(ErrorResponseBody),
101 : NoData,
102 : NoticeResponse(NoticeResponseBody),
103 : NotificationResponse(NotificationResponseBody),
104 : ParameterDescription(ParameterDescriptionBody),
105 : ParameterStatus(ParameterStatusBody),
106 : ParseComplete,
107 : PortalSuspended,
108 : ReadyForQuery(ReadyForQueryBody),
109 : RowDescription(RowDescriptionBody),
110 : }
111 :
112 : impl Message {
113 : #[inline]
114 143 : pub fn parse(buf: &mut BytesMut) -> io::Result<Option<Message>> {
115 143 : if buf.len() < 5 {
116 87 : let to_read = 5 - buf.len();
117 87 : buf.reserve(to_read);
118 87 : return Ok(None);
119 0 : }
120 :
121 56 : let tag = buf[0];
122 56 : let len = (&buf[1..5]).read_u32::<BigEndian>().unwrap();
123 :
124 56 : if len < 4 {
125 0 : return Err(io::Error::new(
126 0 : io::ErrorKind::InvalidInput,
127 0 : "invalid message length: parsing u32",
128 0 : ));
129 0 : }
130 :
131 56 : let total_len = len as usize + 1;
132 56 : if buf.len() < total_len {
133 2 : let to_read = total_len - buf.len();
134 2 : buf.reserve(to_read);
135 2 : return Ok(None);
136 0 : }
137 :
138 54 : let mut buf = Buffer {
139 54 : bytes: buf.split_to(total_len).freeze(),
140 54 : idx: 5,
141 54 : };
142 :
143 54 : let message = match tag {
144 0 : PARSE_COMPLETE_TAG => Message::ParseComplete,
145 0 : BIND_COMPLETE_TAG => Message::BindComplete,
146 0 : CLOSE_COMPLETE_TAG => Message::CloseComplete,
147 0 : NOTIFICATION_RESPONSE_TAG => Message::NotificationResponse(NotificationResponseBody {}),
148 0 : COPY_DONE_TAG => Message::CopyDone,
149 : COMMAND_COMPLETE_TAG => {
150 0 : let tag = buf.read_cstr()?;
151 0 : Message::CommandComplete(CommandCompleteBody { tag })
152 : }
153 0 : COPY_DATA_TAG => Message::CopyData,
154 : DATA_ROW_TAG => {
155 0 : let len = buf.read_u16::<BigEndian>()?;
156 0 : let storage = buf.read_all();
157 0 : Message::DataRow(DataRowBody { storage, len })
158 : }
159 : ERROR_RESPONSE_TAG => {
160 1 : let storage = buf.read_all();
161 1 : Message::ErrorResponse(ErrorResponseBody { storage })
162 : }
163 0 : COPY_IN_RESPONSE_TAG => Message::CopyInResponse,
164 0 : COPY_OUT_RESPONSE_TAG => Message::CopyOutResponse,
165 0 : COPY_BOTH_RESPONSE_TAG => Message::CopyBothResponse,
166 0 : EMPTY_QUERY_RESPONSE_TAG => Message::EmptyQueryResponse,
167 : BACKEND_KEY_DATA_TAG => {
168 0 : let process_id = buf.read_i32::<BigEndian>()?;
169 0 : let secret_key = buf.read_i32::<BigEndian>()?;
170 0 : Message::BackendKeyData(BackendKeyDataBody {
171 0 : process_id,
172 0 : secret_key,
173 0 : })
174 : }
175 0 : NO_DATA_TAG => Message::NoData,
176 : NOTICE_RESPONSE_TAG => {
177 0 : let storage = buf.read_all();
178 0 : Message::NoticeResponse(NoticeResponseBody { storage })
179 : }
180 39 : AUTHENTICATION_TAG => match buf.read_i32::<BigEndian>()? {
181 7 : 0 => Message::AuthenticationOk,
182 0 : 2 => Message::AuthenticationKerberosV5,
183 2 : 3 => Message::AuthenticationCleartextPassword,
184 0 : 5 => Message::AuthenticationMd5Password,
185 0 : 6 => Message::AuthenticationScmCredential,
186 0 : 7 => Message::AuthenticationGss,
187 0 : 8 => Message::AuthenticationGssContinue,
188 0 : 9 => Message::AuthenticationSspi,
189 : 10 => {
190 13 : let storage = buf.read_all();
191 13 : Message::AuthenticationSasl(AuthenticationSaslBody(storage))
192 : }
193 : 11 => {
194 11 : let storage = buf.read_all();
195 11 : Message::AuthenticationSaslContinue(AuthenticationSaslContinueBody(storage))
196 : }
197 : 12 => {
198 6 : let storage = buf.read_all();
199 6 : Message::AuthenticationSaslFinal(AuthenticationSaslFinalBody(storage))
200 : }
201 0 : tag => {
202 0 : return Err(io::Error::new(
203 0 : io::ErrorKind::InvalidInput,
204 0 : format!("unknown authentication tag `{tag}`"),
205 0 : ));
206 : }
207 : },
208 0 : PORTAL_SUSPENDED_TAG => Message::PortalSuspended,
209 : PARAMETER_STATUS_TAG => {
210 7 : let name = buf.read_cstr()?;
211 7 : let value = buf.read_cstr()?;
212 7 : Message::ParameterStatus(ParameterStatusBody { name, value })
213 : }
214 : PARAMETER_DESCRIPTION_TAG => {
215 0 : let len = buf.read_u16::<BigEndian>()?;
216 0 : let storage = buf.read_all();
217 0 : Message::ParameterDescription(ParameterDescriptionBody { storage, len })
218 : }
219 : ROW_DESCRIPTION_TAG => {
220 0 : let len = buf.read_u16::<BigEndian>()?;
221 0 : let storage = buf.read_all();
222 0 : Message::RowDescription(RowDescriptionBody { storage, len })
223 : }
224 : READY_FOR_QUERY_TAG => {
225 7 : let status = buf.read_u8()?;
226 7 : Message::ReadyForQuery(ReadyForQueryBody { status })
227 : }
228 0 : tag => {
229 0 : return Err(io::Error::new(
230 0 : io::ErrorKind::InvalidInput,
231 0 : format!("unknown message tag `{tag}`"),
232 0 : ));
233 : }
234 : };
235 :
236 54 : if !buf.is_empty() {
237 0 : return Err(io::Error::new(
238 0 : io::ErrorKind::InvalidInput,
239 0 : "invalid message length: expected buffer to be empty",
240 0 : ));
241 0 : }
242 :
243 54 : Ok(Some(message))
244 0 : }
245 : }
246 :
247 : struct Buffer {
248 : bytes: Bytes,
249 : idx: usize,
250 : }
251 :
252 : impl Buffer {
253 : #[inline]
254 114 : fn slice(&self) -> &[u8] {
255 114 : &self.bytes[self.idx..]
256 46 : }
257 :
258 : #[inline]
259 54 : fn is_empty(&self) -> bool {
260 54 : self.slice().is_empty()
261 0 : }
262 :
263 : #[inline]
264 14 : fn read_cstr(&mut self) -> io::Result<Bytes> {
265 14 : match memchr(0, self.slice()) {
266 14 : Some(pos) => {
267 14 : let start = self.idx;
268 14 : let end = start + pos;
269 14 : let cstr = self.bytes.slice(start..end);
270 14 : self.idx = end + 1;
271 14 : Ok(cstr)
272 : }
273 0 : None => Err(io::Error::new(
274 0 : io::ErrorKind::UnexpectedEof,
275 0 : "unexpected EOF",
276 0 : )),
277 : }
278 0 : }
279 :
280 : #[inline]
281 31 : fn read_all(&mut self) -> Bytes {
282 31 : let buf = self.bytes.slice(self.idx..);
283 31 : self.idx = self.bytes.len();
284 31 : buf
285 0 : }
286 : }
287 :
288 : impl Read for Buffer {
289 : #[inline]
290 46 : fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
291 46 : let len = {
292 46 : let slice = self.slice();
293 46 : let len = cmp::min(slice.len(), buf.len());
294 46 : buf[..len].copy_from_slice(&slice[..len]);
295 46 : len
296 : };
297 46 : self.idx += len;
298 46 : Ok(len)
299 46 : }
300 : }
301 :
302 : pub struct AuthenticationMd5PasswordBody {
303 : salt: [u8; 4],
304 : }
305 :
306 : impl AuthenticationMd5PasswordBody {
307 : #[inline]
308 0 : pub fn salt(&self) -> [u8; 4] {
309 0 : self.salt
310 0 : }
311 : }
312 :
313 : pub struct AuthenticationSaslBody(Bytes);
314 :
315 : impl AuthenticationSaslBody {
316 : #[inline]
317 13 : pub fn mechanisms(&self) -> SaslMechanisms<'_> {
318 13 : SaslMechanisms(&self.0)
319 0 : }
320 : }
321 :
322 : pub struct SaslMechanisms<'a>(&'a [u8]);
323 :
324 : impl<'a> FallibleIterator for SaslMechanisms<'a> {
325 : type Item = &'a str;
326 : type Error = io::Error;
327 :
328 : #[inline]
329 36 : fn next(&mut self) -> io::Result<Option<&'a str>> {
330 36 : let value_end = find_null(self.0, 0)?;
331 36 : if value_end == 0 {
332 13 : if self.0.len() != 1 {
333 0 : return Err(io::Error::new(
334 0 : io::ErrorKind::InvalidData,
335 0 : "invalid message length: expected to be at end of iterator for sasl",
336 0 : ));
337 0 : }
338 13 : Ok(None)
339 : } else {
340 23 : let value = get_str(&self.0[..value_end])?;
341 23 : self.0 = &self.0[value_end + 1..];
342 23 : Ok(Some(value))
343 : }
344 0 : }
345 : }
346 :
347 : pub struct AuthenticationSaslContinueBody(Bytes);
348 :
349 : impl AuthenticationSaslContinueBody {
350 : #[inline]
351 11 : pub fn data(&self) -> &[u8] {
352 11 : &self.0
353 0 : }
354 : }
355 :
356 : pub struct AuthenticationSaslFinalBody(Bytes);
357 :
358 : impl AuthenticationSaslFinalBody {
359 : #[inline]
360 6 : pub fn data(&self) -> &[u8] {
361 6 : &self.0
362 0 : }
363 : }
364 :
365 : pub struct BackendKeyDataBody {
366 : process_id: i32,
367 : secret_key: i32,
368 : }
369 :
370 : impl BackendKeyDataBody {
371 : #[inline]
372 0 : pub fn process_id(&self) -> i32 {
373 0 : self.process_id
374 0 : }
375 :
376 : #[inline]
377 0 : pub fn secret_key(&self) -> i32 {
378 0 : self.secret_key
379 0 : }
380 : }
381 :
382 : pub struct CommandCompleteBody {
383 : tag: Bytes,
384 : }
385 :
386 : impl CommandCompleteBody {
387 : #[inline]
388 0 : pub fn tag(&self) -> io::Result<&str> {
389 0 : get_str(&self.tag)
390 0 : }
391 : }
392 :
393 : #[derive(Debug)]
394 : pub struct DataRowBody {
395 : storage: Bytes,
396 : len: u16,
397 : }
398 :
399 : impl DataRowBody {
400 : #[inline]
401 0 : pub fn ranges(&self) -> DataRowRanges<'_> {
402 0 : DataRowRanges {
403 0 : buf: &self.storage,
404 0 : len: self.storage.len(),
405 0 : remaining: self.len,
406 0 : }
407 0 : }
408 :
409 : #[inline]
410 0 : pub fn buffer(&self) -> &[u8] {
411 0 : &self.storage
412 0 : }
413 : }
414 :
415 : pub struct DataRowRanges<'a> {
416 : buf: &'a [u8],
417 : len: usize,
418 : remaining: u16,
419 : }
420 :
421 : impl FallibleIterator for DataRowRanges<'_> {
422 : type Item = Option<Range<usize>>;
423 : type Error = io::Error;
424 :
425 : #[inline]
426 0 : fn next(&mut self) -> io::Result<Option<Option<Range<usize>>>> {
427 0 : if self.remaining == 0 {
428 0 : if self.buf.is_empty() {
429 0 : return Ok(None);
430 : } else {
431 0 : return Err(io::Error::new(
432 0 : io::ErrorKind::InvalidInput,
433 0 : "invalid message length: datarowrange is not empty",
434 0 : ));
435 : }
436 0 : }
437 :
438 0 : self.remaining -= 1;
439 0 : let len = self.buf.read_i32::<BigEndian>()?;
440 0 : if len < 0 {
441 0 : Ok(Some(None))
442 : } else {
443 0 : let len = len as usize;
444 0 : if self.buf.len() < len {
445 0 : return Err(io::Error::new(
446 0 : io::ErrorKind::UnexpectedEof,
447 0 : "unexpected EOF",
448 0 : ));
449 0 : }
450 0 : let base = self.len - self.buf.len();
451 0 : self.buf = &self.buf[len..];
452 0 : Ok(Some(Some(base..base + len)))
453 : }
454 0 : }
455 :
456 : #[inline]
457 0 : fn size_hint(&self) -> (usize, Option<usize>) {
458 0 : let len = self.remaining as usize;
459 0 : (len, Some(len))
460 0 : }
461 : }
462 :
463 : pub struct ErrorResponseBody {
464 : storage: Bytes,
465 : }
466 :
467 : impl ErrorResponseBody {
468 : #[inline]
469 1 : pub fn fields(&self) -> ErrorFields<'_> {
470 1 : ErrorFields { buf: &self.storage }
471 0 : }
472 : }
473 :
474 : pub struct ErrorFields<'a> {
475 : buf: &'a [u8],
476 : }
477 :
478 : impl<'a> FallibleIterator for ErrorFields<'a> {
479 : type Item = ErrorField<'a>;
480 : type Error = io::Error;
481 :
482 : #[inline]
483 4 : fn next(&mut self) -> io::Result<Option<ErrorField<'a>>> {
484 4 : let type_ = self.buf.read_u8()?;
485 4 : if type_ == 0 {
486 1 : if self.buf.is_empty() {
487 1 : return Ok(None);
488 : } else {
489 0 : return Err(io::Error::new(
490 0 : io::ErrorKind::InvalidInput,
491 0 : "invalid message length: error fields is not drained",
492 0 : ));
493 : }
494 0 : }
495 :
496 3 : let value_end = find_null(self.buf, 0)?;
497 3 : let value = get_str(&self.buf[..value_end])?;
498 3 : self.buf = &self.buf[value_end + 1..];
499 :
500 3 : Ok(Some(ErrorField { type_, value }))
501 0 : }
502 : }
503 :
504 : pub struct ErrorField<'a> {
505 : type_: u8,
506 : value: &'a str,
507 : }
508 :
509 : impl ErrorField<'_> {
510 : #[inline]
511 3 : pub fn type_(&self) -> u8 {
512 3 : self.type_
513 0 : }
514 :
515 : #[inline]
516 3 : pub fn value(&self) -> &str {
517 3 : self.value
518 0 : }
519 : }
520 :
521 : pub struct NoticeResponseBody {
522 : storage: Bytes,
523 : }
524 :
525 : impl NoticeResponseBody {
526 : #[inline]
527 0 : pub fn fields(&self) -> ErrorFields<'_> {
528 0 : ErrorFields { buf: &self.storage }
529 0 : }
530 :
531 0 : pub fn as_bytes(&self) -> &[u8] {
532 0 : &self.storage
533 0 : }
534 : }
535 :
536 : pub struct NotificationResponseBody {}
537 :
538 : pub struct ParameterDescriptionBody {
539 : storage: Bytes,
540 : len: u16,
541 : }
542 :
543 : impl ParameterDescriptionBody {
544 : #[inline]
545 0 : pub fn parameters(&self) -> Parameters<'_> {
546 0 : Parameters {
547 0 : buf: &self.storage,
548 0 : remaining: self.len,
549 0 : }
550 0 : }
551 : }
552 :
553 : pub struct Parameters<'a> {
554 : buf: &'a [u8],
555 : remaining: u16,
556 : }
557 :
558 : impl FallibleIterator for Parameters<'_> {
559 : type Item = Oid;
560 : type Error = io::Error;
561 :
562 : #[inline]
563 0 : fn next(&mut self) -> io::Result<Option<Oid>> {
564 0 : if self.remaining == 0 {
565 0 : if self.buf.is_empty() {
566 0 : return Ok(None);
567 : } else {
568 0 : return Err(io::Error::new(
569 0 : io::ErrorKind::InvalidInput,
570 0 : "invalid message length: parameters is not drained",
571 0 : ));
572 : }
573 0 : }
574 :
575 0 : self.remaining -= 1;
576 0 : self.buf.read_u32::<BigEndian>().map(Some)
577 0 : }
578 :
579 : #[inline]
580 0 : fn size_hint(&self) -> (usize, Option<usize>) {
581 0 : let len = self.remaining as usize;
582 0 : (len, Some(len))
583 0 : }
584 : }
585 :
586 : pub struct ParameterStatusBody {
587 : name: Bytes,
588 : value: Bytes,
589 : }
590 :
591 : impl ParameterStatusBody {
592 : #[inline]
593 7 : pub fn name(&self) -> io::Result<&str> {
594 7 : get_str(&self.name)
595 0 : }
596 :
597 : #[inline]
598 7 : pub fn value(&self) -> io::Result<&str> {
599 7 : get_str(&self.value)
600 0 : }
601 : }
602 :
603 : pub struct ReadyForQueryBody {
604 : status: u8,
605 : }
606 :
607 : impl ReadyForQueryBody {
608 : #[inline]
609 0 : pub fn status(&self) -> u8 {
610 0 : self.status
611 0 : }
612 : }
613 :
614 : pub struct RowDescriptionBody {
615 : storage: Bytes,
616 : len: u16,
617 : }
618 :
619 : impl RowDescriptionBody {
620 : #[inline]
621 0 : pub fn fields(&self) -> Fields<'_> {
622 0 : Fields {
623 0 : buf: &self.storage,
624 0 : remaining: self.len,
625 0 : }
626 0 : }
627 : }
628 :
629 : pub struct Fields<'a> {
630 : buf: &'a [u8],
631 : remaining: u16,
632 : }
633 :
634 : impl<'a> FallibleIterator for Fields<'a> {
635 : type Item = Field<'a>;
636 : type Error = io::Error;
637 :
638 : #[inline]
639 0 : fn next(&mut self) -> io::Result<Option<Field<'a>>> {
640 0 : if self.remaining == 0 {
641 0 : if self.buf.is_empty() {
642 0 : return Ok(None);
643 : } else {
644 0 : return Err(io::Error::new(
645 0 : io::ErrorKind::InvalidInput,
646 0 : "invalid message length: field is not drained",
647 0 : ));
648 : }
649 0 : }
650 :
651 0 : self.remaining -= 1;
652 0 : let name_end = find_null(self.buf, 0)?;
653 0 : let name = get_str(&self.buf[..name_end])?;
654 0 : self.buf = &self.buf[name_end + 1..];
655 0 : let table_oid = self.buf.read_u32::<BigEndian>()?;
656 0 : let column_id = self.buf.read_i16::<BigEndian>()?;
657 0 : let type_oid = self.buf.read_u32::<BigEndian>()?;
658 0 : let type_size = self.buf.read_i16::<BigEndian>()?;
659 0 : let type_modifier = self.buf.read_i32::<BigEndian>()?;
660 0 : let format = self.buf.read_i16::<BigEndian>()?;
661 :
662 0 : Ok(Some(Field {
663 0 : name,
664 0 : table_oid,
665 0 : column_id,
666 0 : type_oid,
667 0 : type_size,
668 0 : type_modifier,
669 0 : format,
670 0 : }))
671 0 : }
672 : }
673 :
674 : pub struct Field<'a> {
675 : name: &'a str,
676 : table_oid: Oid,
677 : column_id: i16,
678 : type_oid: Oid,
679 : type_size: i16,
680 : type_modifier: i32,
681 : format: i16,
682 : }
683 :
684 : impl<'a> Field<'a> {
685 : #[inline]
686 0 : pub fn name(&self) -> &'a str {
687 0 : self.name
688 0 : }
689 :
690 : #[inline]
691 0 : pub fn table_oid(&self) -> Oid {
692 0 : self.table_oid
693 0 : }
694 :
695 : #[inline]
696 0 : pub fn column_id(&self) -> i16 {
697 0 : self.column_id
698 0 : }
699 :
700 : #[inline]
701 0 : pub fn type_oid(&self) -> Oid {
702 0 : self.type_oid
703 0 : }
704 :
705 : #[inline]
706 0 : pub fn type_size(&self) -> i16 {
707 0 : self.type_size
708 0 : }
709 :
710 : #[inline]
711 0 : pub fn type_modifier(&self) -> i32 {
712 0 : self.type_modifier
713 0 : }
714 :
715 : #[inline]
716 0 : pub fn format(&self) -> i16 {
717 0 : self.format
718 0 : }
719 : }
720 :
721 : #[inline]
722 39 : fn find_null(buf: &[u8], start: usize) -> io::Result<usize> {
723 39 : match memchr(0, &buf[start..]) {
724 39 : Some(pos) => Ok(pos + start),
725 0 : None => Err(io::Error::new(
726 0 : io::ErrorKind::UnexpectedEof,
727 0 : "unexpected EOF",
728 0 : )),
729 : }
730 0 : }
731 :
732 : #[inline]
733 40 : fn get_str(buf: &[u8]) -> io::Result<&str> {
734 40 : str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
735 0 : }
|