File: Socket.sml

package info (click to toggle)
polyml 5.7.1-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 40,616 kB
  • sloc: cpp: 44,142; ansic: 26,963; sh: 22,002; asm: 13,486; makefile: 602; exp: 525; python: 253; awk: 91
file content (678 lines) | stat: -rw-r--r-- 27,251 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
(*
    Title:      Standard Basis Library: Generic Sockets
    Author:     David Matthews
    Copyright   David Matthews 2000, 2005, 2015-16

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License version 2.1 as published by the Free Software Foundation.
    
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*)

signature SOCKET =
sig
    type ('af,'sock_type) sock
    type 'af sock_addr
    type dgram
    type 'mode stream
    type passive
    type active

    structure AF :
    sig
        type addr_family = NetHostDB.addr_family
        val list : unit -> (string * addr_family) list
        val toString   : addr_family -> string
        val fromString : string -> addr_family option
    end

    structure SOCK :
    sig
        eqtype sock_type
        val stream : sock_type
        val dgram : sock_type
        val list : unit -> (string * sock_type) list
        val toString   : sock_type -> string
        val fromString : string -> sock_type option
    end

    structure Ctl :
    sig
         val getDEBUG : ('af, 'sock_type) sock -> bool
         val setDEBUG : ('af, 'sock_type) sock * bool -> unit
         val getREUSEADDR : ('af, 'sock_type) sock -> bool
         val setREUSEADDR : ('af, 'sock_type) sock * bool -> unit
         val getKEEPALIVE : ('af, 'sock_type) sock -> bool
         val setKEEPALIVE : ('af, 'sock_type) sock * bool -> unit
         val getDONTROUTE : ('af, 'sock_type) sock -> bool
         val setDONTROUTE : ('af, 'sock_type) sock * bool -> unit
         val getLINGER : ('af, 'sock_type) sock -> Time.time option
         val setLINGER : ('af, 'sock_type) sock * Time.time option -> unit
         val getBROADCAST : ('af, 'sock_type) sock -> bool
         val setBROADCAST : ('af, 'sock_type) sock * bool -> unit
         val getOOBINLINE : ('af, 'sock_type) sock -> bool
         val setOOBINLINE : ('af, 'sock_type) sock * bool  -> unit
         val getSNDBUF : ('af, 'sock_type) sock -> int
         val setSNDBUF : ('af, 'sock_type) sock * int -> unit
         val getRCVBUF : ('af, 'sock_type) sock -> int
         val setRCVBUF : ('af, 'sock_type) sock * int -> unit
         val getTYPE : ('af, 'sock_type) sock -> SOCK.sock_type
         val getERROR : ('af, 'sock_type) sock -> bool
         val getPeerName : ('af, 'sock_type) sock -> 'af sock_addr
         val getSockName : ('af, 'sock_type) sock -> 'af sock_addr
         val getNREAD : ('af, 'sock_type) sock -> int
         val getATMARK : ('af, active stream) sock -> bool
         end

     val sameAddr : 'af sock_addr * 'af sock_addr -> bool
     val familyOfAddr : 'af sock_addr -> AF.addr_family

     val bind : ('af, 'sock_type) sock * 'af sock_addr -> unit
     val listen : ('af, passive stream) sock * int -> unit
     val accept : ('af, passive stream) sock
                    -> ('af, active stream) sock * 'af sock_addr
     val acceptNB : ('af, passive stream) sock
                    -> (('af, active stream) sock * 'af sock_addr) option
     val connect : ('af, 'sock_type) sock * 'af sock_addr -> unit
     val connectNB : ('af, 'sock_type) sock * 'af sock_addr -> bool
     val close : ('af, 'sock_type) sock -> unit

     datatype shutdown_mode
       = NO_RECVS
       | NO_SENDS
       | NO_RECVS_OR_SENDS

     val shutdown : ('af, 'sock_type stream) sock * shutdown_mode -> unit

     type sock_desc
     val sockDesc : ('af, 'sock_type) sock -> sock_desc
     val sameDesc: sock_desc * sock_desc -> bool

     
     val select:
            { rds: sock_desc list, wrs : sock_desc list, exs : sock_desc list, timeout: Time.time option } ->
            { rds: sock_desc list, wrs : sock_desc list, exs : sock_desc list }
     
     val ioDesc : ('af, 'sock_type) sock -> OS.IO.iodesc

     type out_flags = {don't_route : bool, oob : bool}
     type in_flags = {peek : bool, oob : bool}

     val sendVec : ('af, active stream) sock * Word8VectorSlice.slice -> int
     val sendArr : ('af, active stream) sock * Word8ArraySlice.slice -> int
     val sendVec' : ('af, active stream) sock * Word8VectorSlice.slice
                      * out_flags -> int
     val sendArr' : ('af, active stream) sock * Word8ArraySlice.slice
                      * out_flags -> int
     val sendVecNB : ('af, active stream) sock * Word8VectorSlice.slice -> int option
     val sendArrNB : ('af, active stream) sock * Word8ArraySlice.slice -> int option
     val sendVecNB' : ('af, active stream) sock * Word8VectorSlice.slice
                      * out_flags -> int option
     val sendArrNB' : ('af, active stream) sock * Word8ArraySlice.slice
                      * out_flags -> int option
                      
     val recvVec : ('af, active stream) sock * int -> Word8Vector.vector
     val recvArr : ('af, active stream) sock  * Word8ArraySlice.slice -> int
     val recvVec' : ('af, active stream) sock * int * in_flags
                      -> Word8Vector.vector
     val recvArr' : ('af, active stream) sock * Word8ArraySlice.slice
                      * in_flags -> int
     val recvVecNB : ('af, active stream) sock * int -> Word8Vector.vector option
     val recvArrNB : ('af, active stream) sock  * Word8ArraySlice.slice -> int option
     val recvVecNB' : ('af, active stream) sock * int * in_flags
                      -> Word8Vector.vector option
     val recvArrNB' : ('af, active stream) sock * Word8ArraySlice.slice
                      * in_flags -> int option

     val sendVecTo : ('af, dgram) sock * 'af sock_addr
                       * Word8VectorSlice.slice -> unit
     val sendArrTo : ('af, dgram) sock * 'af sock_addr
                       * Word8ArraySlice.slice -> unit
     val sendVecTo' : ('af, dgram) sock * 'af sock_addr
                        * Word8VectorSlice.slice * out_flags -> unit
     val sendArrTo' : ('af, dgram) sock * 'af sock_addr
                        * Word8ArraySlice.slice * out_flags -> unit
     val sendVecToNB : ('af, dgram) sock * 'af sock_addr
                       * Word8VectorSlice.slice -> bool
     val sendArrToNB : ('af, dgram) sock * 'af sock_addr
                       * Word8ArraySlice.slice -> bool
     val sendVecToNB' : ('af, dgram) sock * 'af sock_addr
                        * Word8VectorSlice.slice * out_flags -> bool
     val sendArrToNB' : ('af, dgram) sock * 'af sock_addr
                        * Word8ArraySlice.slice * out_flags -> bool

     val recvVecFrom : ('af, dgram) sock * int
                         -> Word8Vector.vector * 'sock_type sock_addr
     val recvArrFrom : ('af, dgram) sock * Word8ArraySlice.slice
                         -> int * 'af sock_addr
     val recvVecFrom' : ('af, dgram) sock * int * in_flags
                          -> Word8Vector.vector * 'sock_type sock_addr
     val recvArrFrom' : ('af, dgram) sock * Word8ArraySlice.slice
                          * in_flags -> int * 'af sock_addr
     val recvVecFromNB : ('af, dgram) sock * int
                         -> (Word8Vector.vector * 'sock_type sock_addr) option
     val recvArrFromNB : ('af, dgram) sock * Word8ArraySlice.slice
                         -> (int * 'af sock_addr) option
     val recvVecFromNB' : ('af, dgram) sock * int * in_flags
                          -> (Word8Vector.vector * 'sock_type sock_addr) option
     val recvArrFromNB' : ('af, dgram) sock * Word8ArraySlice.slice
                          * in_flags -> (int * 'af sock_addr) option
end;

structure Socket :> SOCKET =
struct
    (* We don't really need an implementation for these.  *)
    (* TODO: We should really pull the definition of the sock type into a common structure so
       it can be shared by the various socket structures.  In fact it doesn't matter since the
       unary constructor here is compiled as an identity so the underlying representation of
       "SOCK x" will be the same as "x". *)
    datatype ('af,'sock_type) sock = SOCK of OS.IO.iodesc
    and dgram = DGRAM
    and 'mode stream = STREAM
    and passive = PASSIVE
    and active = ACTIVE

    local
        val netCall: int * word -> word = RunCall.rtsCallFull2 "PolyNetworkGeneral"
    in
        fun doNetCall(i: int, arg:'a):'b =
            RunCall.unsafeCast(netCall(i, RunCall.unsafeCast arg))
    end

    structure AF =
    struct
        type addr_family = NetHostDB.addr_family

        local
            val doCall: int*unit -> (string * addr_family) list
                 = doNetCall
        in
            fun list () = doCall(11, ())
        end

        fun toString (af: addr_family) =
        let
            val afs = list()
        in
            (* Do a linear search on the list - it's small. *)
            case List.find (fn (_, af') => af=af') afs of
                NONE => raise OS.SysErr("Missing address family", NONE)
            |   SOME (s, _) => s
        end

        fun fromString s =
        let
            val afs = list()
        in
            (* Do a linear search on the list - it's small. *)
            case List.find (fn (s', _) => s=s') afs of
                NONE => NONE
            |   SOME (_, af) => SOME af
        end
    end

    structure SOCK =
    struct
        datatype sock_type = SOCKTYPE of int

        local
            val doCall: int*unit -> (string * sock_type) list
                 = doNetCall
        in
            fun list () = doCall(12, ())
        end

        fun toString (sk: sock_type) =
        let
            val sks = list()
        in
            (* Do a linear search on the list - it's small. *)
            case List.find (fn (_, sk') => sk=sk') sks of
                NONE => raise OS.SysErr("Missing socket type", NONE)
            |   SOME (s, _) => s
        end

        fun fromString s =
        let
            val sks = list()
        in
            (* Do a linear search on the list - it's small. *)
            case List.find (fn (s', _) => s=s') sks of
                NONE => NONE
            |   SOME (_, sk) => SOME sk
        end

        (* We assume that both of these at least are in the table. *)
        val stream =
            case fromString "STREAM" of
                NONE => raise OS.SysErr("Missing socket type", NONE)
            |   SOME s => s

        val dgram =
            case fromString "DGRAM" of
                NONE => raise OS.SysErr("Missing socket type", NONE)
            |   SOME s => s
    end

    (* Socket addresses are implemented as strings. *)
    datatype 'af sock_addr = SOCKADDR of Word8Vector.vector

    (* Note: The definition did not make these equality type variables.
       The assumption is probably that it works much like equality on
       references. *)
    fun sameAddr (SOCKADDR a, SOCKADDR b) = a = b

    (* Many of these calls involve type variables.  We have to use a cast to
       get the types right. *)
    local
        val doCall = doNetCall
    in
        fun familyOfAddr (sa: 'af sock_addr) = doCall(39, RunCall.unsafeCast sa)
    end

    structure Ctl =
    struct
        local
            val doCall1 = doNetCall
            val doCall2 = doNetCall
        in
            fun getOpt (i:int) (SOCK s) = doCall1(i, s)
            fun setOpt (i: int) (SOCK s, b: bool) = doCall2(i, (s, b))
        end

        fun getDEBUG s = getOpt 18 s
        and setDEBUG s = setOpt 17 s
        and getREUSEADDR s = getOpt 20 s
        and setREUSEADDR s = setOpt 19 s
        and getKEEPALIVE s = getOpt 22 s
        and setKEEPALIVE s = setOpt 21 s
        and getDONTROUTE s = getOpt 24 s
        and setDONTROUTE s = setOpt 23 s
        and getBROADCAST s = getOpt 26 s
        and setBROADCAST s = setOpt 25 s
        and getOOBINLINE s = getOpt 28 s
        and setOOBINLINE s = setOpt 27 s
        and getERROR s = getOpt 34 s
        and getATMARK s = getOpt 45 s

        local
            val doCall1 = doNetCall
            val doCall2 = doNetCall
        in
            fun getSNDBUF (SOCK s) = doCall1(30, s)
            fun setSNDBUF (SOCK s, i: int) = doCall2(29, (s, i))
            fun getRCVBUF (SOCK s) = doCall1(32, s)
            fun setRCVBUF (SOCK s, i: int) = doCall2(31, (s, i))
            fun getTYPE (SOCK s) = SOCK.SOCKTYPE(doCall1(33, s))
                    
            fun getNREAD (SOCK s) = doCall1(44, s)

            fun getLINGER (SOCK s): Time.time option =
            let
                val lTime = doCall1(36, s)
            in
                if lTime < 0 then NONE else SOME(Time.fromSeconds(LargeInt.fromInt lTime))
            end

            fun setLINGER (SOCK s, NONE) =
                (
                    doCall2(35, (s, ~1))
                )
            |   setLINGER (SOCK s, SOME t) =
                let
                    val lTime = LargeInt.toInt(Time.toSeconds t)
                in
                    if lTime < 0
                    then raise OS.SysErr("Invalid time", NONE)
                    else doCall2(35, (s, lTime))
                end
        end

        local
            val doCall = doNetCall
        in
            fun getPeerName (SOCK s): 'af sock_addr = RunCall.unsafeCast(doCall(37, s))

            fun getSockName (SOCK s): 'af sock_addr = RunCall.unsafeCast(doCall(38, s))
        end
        end (* Ctl *)


    (* Run an operation in non-blocking mode.  This catches EWOULDBLOCK and returns NONE,
       otherwise returns SOME result.  Other exceptions are passed back as normal. *)
    val nonBlockingCall = LibraryIOSupport.nonBlocking

    local
        val doCall = doNetCall
    in
        fun accept (SOCK s) = RunCall.unsafeCast(doCall (46, s))
    end

    local
        val doCall = doNetCall
        fun acc sock = doCall (58, RunCall.unsafeCast sock)
    in
        fun acceptNB sock = RunCall.unsafeCast(nonBlockingCall acc sock)
    end

    local
        val doCall = doNetCall
    in
        fun bind (SOCK s, a) = doCall (47, RunCall.unsafeCast (s, a))
    end

    local
        val doCall = doNetCall
    in
        fun connect (SOCK s, a) = doCall (48, RunCall.unsafeCast (s, a))
    end

    local
        val doCall = doNetCall
        fun connct sa = doCall (59, RunCall.unsafeCast sa)
    in
        fun connectNB (SOCK s, a) =
            case nonBlockingCall connct (s,a) of SOME () => true | NONE => false
    end

    fun listen (SOCK s, b) =
        doNetCall (49, (s, b))

    (* We use the normal "close" for streams. *)
    local
        val doCall = RunCall.rtsCallFull3 "PolyBasicIOGeneral"
    in
        fun close (SOCK strm): unit = doCall(7, strm, 0)
    end

    datatype shutdown_mode = NO_RECVS | NO_SENDS | NO_RECVS_OR_SENDS

    local
        val doCall = doNetCall
    in
        fun shutdown (SOCK s, mode) =
        let
            val m =
                case mode of
                    NO_RECVS => 1
                 |  NO_SENDS => 2
                 |  NO_RECVS_OR_SENDS => 3
        in
            doCall (50, (s, m))
        end
    end

    (* The IO descriptor is the underlying socket. *)
    fun ioDesc (SOCK s) = s;

    type out_flags = {don't_route : bool, oob : bool}
    type in_flags = {peek : bool, oob : bool}
    type 'a buf = {buf : 'a, i : int, sz : int option}

    local
        val nullOut = { don't_route = false, oob = false }
        and nullIn = { peek = false, oob = false }

        (* This implementation is copied from the implementation of
           Word8Array.array and Word8Vector.vector. *)
        type address = LibrarySupport.address
        datatype vector = datatype LibrarySupport.Word8Array.vector
        datatype array = datatype LibrarySupport.Word8Array.array
        val wordSize = LibrarySupport.wordSize

        (* Send the data from an array or vector.  Note: the underlying RTS function
           deals with the special case of sending a single byte vector where the
           "address" is actually the byte itself. *)
        local
            val doCall = doNetCall
            fun doSend i a = doCall (i, a)
        in
            fun send (SOCK sock, base: address, offset: int, length: int, rt: bool, oob: bool): int =
                doSend 51 (sock, base, offset, length, rt, oob)
    
            fun sendNB (SOCK sock, base: address, offset: int, length: int, rt: bool, oob: bool): int option =
                nonBlockingCall (doSend 60) (sock, base, offset, length, rt, oob)
        end

        local
            (* Although the underlying call returns the number of bytes written the
               ML functions now return unit. *)
            val doCall = doNetCall
            fun doSendTo i a = doCall (i, a)
        in
            fun sendTo (SOCK sock, addr, base: address, offset: int, length: int, rt: bool, oob: bool): unit =
                doSendTo 52 (RunCall.unsafeCast(sock, addr, base, offset, length, rt, oob))
    
            fun sendToNB (SOCK sock, addr, base: address, offset: int, length: int, rt: bool, oob: bool): bool =
                case nonBlockingCall (doSendTo 61) (RunCall.unsafeCast(sock, addr, base, offset, length, rt, oob)) of
                    NONE => false | SOME _ => true
        end

        local
            val doCall = doNetCall
            fun doRecv i a = doCall (i, a)
        in
            (* Receive the data into an array. *)
            fun recv (SOCK sock, base: address, offset: int, length: int, peek: bool, oob: bool): int =
                doRecv 53 (RunCall.unsafeCast(sock, base, offset, length, peek, oob))

            fun recvNB (SOCK sock, base: address, offset: int, length: int, peek: bool, oob: bool): int option =
                nonBlockingCall (doRecv 62) (RunCall.unsafeCast(sock, base, offset, length, peek, oob))
        end

        local
            val doCall = doNetCall
            fun doRecvFrom i a = doCall (i, a)
        in 
            fun recvFrom (SOCK sock, base: address, offset: int, length: int, peek: bool, oob: bool) =
                RunCall.unsafeCast(doRecvFrom 54 (RunCall.unsafeCast (sock, base, offset, length, peek, oob)))

            fun recvFromNB (SOCK sock, base: address, offset: int, length: int, peek: bool, oob: bool) =
                RunCall.unsafeCast(nonBlockingCall (doRecvFrom 63) (RunCall.unsafeCast (sock, base, offset, length, peek, oob)))
        end
    in
        fun sendVec' (sock, slice: Word8VectorSlice.slice, {don't_route, oob}) =
        let
            val (v, i, length) = Word8VectorSlice.base slice
        in
            send(sock, LibrarySupport.w8vectorAsAddress v, i + Word.toInt wordSize, length, don't_route, oob)
        end
        and sendVec (sock, vbuff) = sendVec'(sock, vbuff, nullOut)
        
        fun sendVecNB' (sock, slice: Word8VectorSlice.slice, {don't_route, oob}) =
        let
            val (v, i, length) = Word8VectorSlice.base slice
        in
            sendNB(sock, LibrarySupport.w8vectorAsAddress v, i + Word.toInt wordSize, length, don't_route, oob)
        end
        and sendVecNB (sock, vbuff) = sendVecNB'(sock, vbuff, nullOut)
    
        fun sendArr' (sock, slice: Word8ArraySlice.slice, {don't_route, oob}) =
        let
            val (Array(_, v), i, length) = Word8ArraySlice.base slice
        in
            send(sock, v, i, length, don't_route, oob)
        end
        and sendArr (sock, vbuff) = sendArr'(sock, vbuff, nullOut)
        
        fun sendArrNB' (sock, slice: Word8ArraySlice.slice, {don't_route, oob}) =
        let
            val (Array(_, v), i, length) = Word8ArraySlice.base slice
        in
            sendNB(sock, v, i, length, don't_route, oob)
        end
        and sendArrNB (sock, vbuff) = sendArrNB'(sock, vbuff, nullOut)
    
        fun sendVecTo' (sock, addr, slice: Word8VectorSlice.slice, {don't_route, oob}) =
        let
            val (v, i, length) = Word8VectorSlice.base slice
        in
            sendTo(sock, addr, LibrarySupport.w8vectorAsAddress v, i + Word.toInt wordSize, length, don't_route, oob)
        end
        and sendVecTo (sock, addr, vbuff) = sendVecTo'(sock, addr, vbuff, nullOut)

        fun sendVecToNB' (sock, addr, slice: Word8VectorSlice.slice, {don't_route, oob}) =
        let
            val (v, i, length) = Word8VectorSlice.base slice
        in
            sendToNB(sock, addr, LibrarySupport.w8vectorAsAddress v, i + Word.toInt wordSize, length, don't_route, oob)
        end
        and sendVecToNB (sock, addr, vbuff) = sendVecToNB'(sock, addr, vbuff, nullOut)

        fun sendArrTo' (sock, addr, slice: Word8ArraySlice.slice, {don't_route, oob}) =
        let
            val (Array(_, v), i, length) = Word8ArraySlice.base slice
        in
            sendTo(sock, addr, v, i, length, don't_route, oob)
        end
        and sendArrTo (sock, addr, vbuff) = sendArrTo'(sock, addr, vbuff, nullOut)

        fun sendArrToNB' (sock, addr, slice: Word8ArraySlice.slice, {don't_route, oob}) =
        let
            val (Array(_, v), i, length) = Word8ArraySlice.base slice
        in
            sendToNB(sock, addr, v, i, length, don't_route, oob)
        end
        and sendArrToNB (sock, addr, vbuff) = sendArrToNB'(sock, addr, vbuff, nullOut)

        fun recvArr' (sock, slice: Word8ArraySlice.slice, {peek, oob}) =
        let
            val (Array(_, v), i, length) = Word8ArraySlice.base slice
        in
            recv(sock, v, i, length, peek, oob)
        end
        and recvArr (sock, vbuff) = recvArr'(sock, vbuff, nullIn)

        fun recvArrNB' (sock, slice: Word8ArraySlice.slice, {peek, oob}) =
        let
            val (Array(_, v), i, length) = Word8ArraySlice.base slice
        in
            recvNB(sock, v, i, length, peek, oob)
        end
        and recvArrNB (sock, vbuff) = recvArrNB'(sock, vbuff, nullIn)
    
        (* To receive a vector first create an array, read into it,
           then copy it to a new vector.  This does involve extra copying
           but it probably doesn't matter too much. *)
        fun recvVec' (sock, size, flags) =
        let
            val arr = Word8Array.array(size, 0w0);
            val recvd = recvArr'(sock, Word8ArraySlice.full arr, flags)
        in
            Word8ArraySlice.vector(Word8ArraySlice.slice(arr, 0, SOME recvd))
        end
        and recvVec (sock, size) = recvVec'(sock, size, nullIn)

        fun recvVecNB' (sock, size, flags) =
        let
            val arr = Word8Array.array(size, 0w0);
        in
            case recvArrNB'(sock, Word8ArraySlice.full arr, flags) of
                NONE => NONE
            |   SOME recvd => SOME(Word8ArraySlice.vector(Word8ArraySlice.slice(arr, 0, SOME recvd)))
        end
        and recvVecNB (sock, size) = recvVecNB'(sock, size, nullIn)

        fun recvArrFrom' (sock, slice: Word8ArraySlice.slice, {peek, oob}) =
        let
            val (Array(_, v), i, length) = Word8ArraySlice.base slice
        in
            recvFrom(sock, v, i, length, peek, oob)
        end
        and recvArrFrom (sock, abuff) = recvArrFrom'(sock, abuff, nullIn)


        fun recvArrFromNB' (sock, slice: Word8ArraySlice.slice, {peek, oob}) =
        let
            val (Array(_, v), i, length) = Word8ArraySlice.base slice
        in
            recvFromNB(sock, v, i, length, peek, oob)
        end
        and recvArrFromNB (sock, abuff) = recvArrFromNB'(sock, abuff, nullIn)

        fun recvVecFrom' (sock, size, flags) =
        let
            val arr = Word8Array.array(size, 0w0);
            val (rcvd, addr) =
                recvArrFrom'(sock, Word8ArraySlice.full arr, flags)
        in
            (Word8ArraySlice.vector(Word8ArraySlice.slice(arr, 0, SOME rcvd)), addr)
        end
        and recvVecFrom (sock, size) = recvVecFrom'(sock, size, nullIn)

        fun recvVecFromNB' (sock, size, flags) =
        let
            val arr = Word8Array.array(size, 0w0);
        in
            case recvArrFromNB'(sock, Word8ArraySlice.full arr, flags) of
                NONE => NONE
            |   SOME (rcvd, addr) =>
                    SOME (Word8ArraySlice.vector(Word8ArraySlice.slice(arr, 0, SOME rcvd)), addr)           
        end
        and recvVecFromNB (sock, size) = recvVecFromNB'(sock, size, nullIn)

    end

    (* "select" call. *)
    datatype sock_desc = SOCKDESC of OS.IO.iodesc
    fun sockDesc (SOCK sock) = SOCKDESC sock (* Create a socket descriptor from a socket. *)
    fun sameDesc (SOCKDESC a, SOCKDESC b) = a = b

    local
        (* The underlying call takes three arrays and updates them with the sockets that are
           in the appropriate state.  It sets inactive elements to ~1. *)
        val doIo: int * (OS.IO.iodesc Vector.vector * OS.IO.iodesc Vector.vector * OS.IO.iodesc Vector.vector * Time.time) ->
                    OS.IO.iodesc Vector.vector * OS.IO.iodesc Vector.vector * OS.IO.iodesc Vector.vector
             = doNetCall
    in
        fun sys_select_block(rds, wrs, exs) = doIo(64, (rds, wrs, exs, Time.zeroTime))
        fun sys_select_poll(rds, wrs, exs) = doIo(65, (rds, wrs, exs, Time.zeroTime))
        (* The time parameter for a wait is the absolute time when the timeout expires. *)
        and sys_select_wait (rds, wrs, exs, t) = doIo(66, (rds, wrs, exs, t))
    end
    
    fun select { rds: sock_desc list, wrs : sock_desc list, exs : sock_desc list, timeout: Time.time option } :
            { rds: sock_desc list, wrs : sock_desc list, exs : sock_desc list } =
    let
        fun sockDescToDesc(SOCKDESC sock) = sock
        (* Create the initial vectors. *)
        val rdVec: OS.IO.iodesc Vector.vector = Vector.fromList(map sockDescToDesc rds)
        val wrVec: OS.IO.iodesc Vector.vector = Vector.fromList(map sockDescToDesc wrs)
        val exVec: OS.IO.iodesc Vector.vector = Vector.fromList(map sockDescToDesc exs)
        open Time
        val (rdResult, wrResult, exResult) =
            (* Do the approriate select. *)
            case timeout of
                NONE => sys_select_block(rdVec, wrVec, exVec)
            |   SOME t => if t <= Time.zeroTime
                          then sys_select_poll(rdVec, wrVec, exVec)
                          else sys_select_wait(rdVec, wrVec, exVec, t + Time.now());
        (* Function to create the results. *)
        fun getResults v = Vector.foldr (fn (sd, l) => SOCKDESC sd :: l) [] v
    in
        (* Convert the results. *)
        { rds = getResults rdResult, wrs = getResults wrResult, exs = getResults exResult }
    end

end;

local
    (* Install the pretty printer for Socket.AF.addr_family
       This must be done outside
       the structure if we use opaque matching. *)
    fun printAF _ _ x = PolyML.PrettyString(Socket.AF.toString x)
    fun printSK _ _ x = PolyML.PrettyString(Socket.SOCK.toString x)
    fun prettySocket _ _ (_: ('a, 'b) Socket.sock) = PolyML.PrettyString "?"
in
    val () = PolyML.addPrettyPrinter printAF
    val () = PolyML.addPrettyPrinter printSK
    val () = PolyML.addPrettyPrinter prettySocket
end;