Merged master into branch with changes for std.string.

This commit is contained in:
jmdavis 2011-06-18 01:15:56 -07:00
commit fabd38658f
11 changed files with 348 additions and 174 deletions

View file

@ -54,7 +54,7 @@ DOCSRC = ../d-programming-language.org
WEBSITE_DIR = ../web
DOC_OUTPUT_DIR = $(WEBSITE_DIR)/phobos-prerelease
BIGDOC_OUTPUT_DIR = /tmp
SRC_DOCUMENTABLES = index.d $(addsuffix .d,$(STD_MODULES) $(EXTRA_DOCUMENTABLES))
SRC_DOCUMENTABLES = index.d $(addsuffix .d,$(STD_MODULES) $(STD_NET_MODULES) $(EXTRA_DOCUMENTABLES))
STDDOC = $(DOCSRC)/std.ddoc
BIGSTDDOC = $(DOCSRC)/std_consolidated.ddoc
DDOCFLAGS=-m$(MODEL) -d -c -o- -version=StdDdoc -I$(DRUNTIME_PATH)/import $(DMDEXTRAFLAGS)
@ -317,6 +317,9 @@ $(DOC_OUTPUT_DIR)/std_c_linux_%.html : std/c/linux/%.d $(STDDOC)
$(DOC_OUTPUT_DIR)/std_c_windows_%.html : std/c/windows/%.d $(STDDOC)
$(DDOC) $(DDOCFLAGS) -Df$@ $<
$(DOC_OUTPUT_DIR)/std_net_%.html : std/net/%.d $(STDDOC)
$(DDOC) $(DDOCFLAGS) $(STDDOC) -Df$@ $<
$(DOC_OUTPUT_DIR)/etc_c_%.html : etc/c/%.d $(STDDOC)
$(DDOC) $(DDOCFLAGS) $(STDDOC) -Df$@ $<

View file

@ -3310,10 +3310,10 @@ unittest
* Example:
----
string s = "abcdef";
assert(findSkip("abcdef", "cd") && s == "ef");
assert(findSkip(s, "cd") && s == "ef");
s = "abcdef";
assert(!findSkip("abcdef", "cxd") && s == "abcdef");
assert(findSkip("abcdef", "def") && s.empty);
assert(!findSkip(s, "cxd") && s == "abcdef");
assert(findSkip(s, "def") && s.empty);
----
*/
bool findSkip(alias pred = "a == b", R1, R2)(ref R1 haystack, R2 needle)

View file

@ -6,6 +6,7 @@
*/
module std.c.freebsd.socket;
public import core.sys.posix.netdb;
import core.sys.posix.sys.socket;
extern(C):
@ -41,38 +42,3 @@ enum // <netinet/in.h>
INADDR_LOOPBACK = 0x7f000001,
INADDR_NONE = 0xffffffff,
}
/*========== <netdb.h> ==========*/
struct hostent
{
char* h_name;
char** h_aliases;
int h_addrtype;
int h_length;
char** h_addr_list;
}
struct servent
{
char* s_name;
char** s_aliases;
int s_port;
char* s_proto;
}
struct protoent
{
char* p_name;
char** p_aliases;
int p_proto;
}
hostent* gethostbyaddr(in void*, socklen_t, int); // obsolete
hostent* gethostbyname(in char*); // obsolete
protoent* getprotobyname(in char *);
protoent* getprotobynumber(int);
servent* getservbyname(in char*, in char*);
servent* getservbyport(int, in char*);

View file

@ -8,6 +8,7 @@ module std.c.linux.socket;
private import core.stdc.stdint;
public import core.sys.posix.arpa.inet;
public import core.sys.posix.netdb;
public import core.sys.posix.netinet.tcp;
public import core.sys.posix.netinet.in_;
public import core.sys.posix.sys.select;
@ -40,46 +41,8 @@ enum: int
IPPROTO_MAX = 256,
}
struct protoent
{
char* p_name;
char** p_aliases;
int32_t p_proto;
}
protoent* getprotobyname(in char* name);
protoent* getprotobynumber(int number);
struct servent
{
char* s_name;
char** s_aliases;
int32_t s_port;
char* s_proto;
}
servent* getservbyname(in char* name, in char* proto);
servent* getservbyport(int port, in char* proto);
struct hostent
{
char* h_name;
char** h_aliases;
int32_t h_addrtype;
int32_t h_length;
char** h_addr_list;
char* h_addr()
{
return h_addr_list[0];
}
}
hostent* gethostbyname(in char* name);
int gethostbyname_r(in char* name, hostent* ret, void* buf, size_t buflen, hostent** result, int* h_errnop);
int gethostbyname2_r(in char* name, int af, hostent* ret, void* buf, size_t buflen, hostent** result, int* h_errnop);
hostent* gethostbyaddr(void* addr, int len, int type);
enum: int
{

View file

@ -8,6 +8,7 @@ module std.c.osx.socket;
private import core.stdc.stdint;
public import core.sys.posix.arpa.inet;
public import core.sys.posix.netdb;
public import core.sys.posix.netinet.tcp;
public import core.sys.posix.netinet.in_;
public import core.sys.posix.sys.select;
@ -40,46 +41,8 @@ enum: int
IPPROTO_MAX = 256,
}
struct protoent
{
char* p_name;
char** p_aliases;
int32_t p_proto;
}
protoent* getprotobyname(in char* name);
protoent* getprotobynumber(int number);
struct servent
{
char* s_name;
char** s_aliases;
int32_t s_port;
char* s_proto;
}
servent* getservbyname(in char* name, in char* proto);
servent* getservbyport(int port, in char* proto);
struct hostent
{
char* h_name;
char** h_aliases;
int32_t h_addrtype;
int32_t h_length;
char** h_addr_list;
char* h_addr()
{
return h_addr_list[0];
}
}
hostent* gethostbyname(in char* name);
int gethostbyname_r(in char* name, hostent* ret, void* buf, size_t buflen, hostent** result, int* h_errnop);
int gethostbyname2_r(in char* name, int af, hostent* ret, void* buf, size_t buflen, hostent** result, int* h_errnop);
hostent* gethostbyaddr(void* addr, int len, int type);
// Not defined in OSX, but we'll use them anyway
enum: int

View file

@ -38,6 +38,8 @@ const int IOCPARM_MASK = 0x7F;
const int IOC_IN = cast(int)0x80000000;
const int FIONBIO = cast(int)(IOC_IN | ((UINT.sizeof & IOCPARM_MASK) << 16) | (102 << 8) | 126);
enum NI_MAXHOST = 1025;
enum NI_MAXSERV = 32;
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
int WSACleanup();

View file

@ -19,6 +19,90 @@ import std.traits;
/** Helper function that returns a _complex number with the specified
real and imaginary parts.
If neither $(D re) nor $(D im) are floating-point numbers, this
function returns a $(D Complex!double). Otherwise, the return type
is deduced using $(D std.traits.CommonType!(R, I)).
Examples:
---
auto c = complex(2.0);
static assert (is(typeof(c) == Complex!double));
assert (c.re == 2.0);
assert (c.im == 0.0);
auto w = complex(2);
static assert (is(typeof(w) == Complex!double));
assert (w == c);
auto z = complex(1, 3.14L);
static assert (is(typeof(z) == Complex!real));
assert (z.re == 1.0L);
assert (z.im == 3.14L);
---
*/
auto complex(T)(T re) @safe pure nothrow if (is(T : double))
{
static if (isFloatingPoint!T)
return Complex!T(re, 0);
else
return Complex!double(re, 0);
}
/// ditto
auto complex(R, I)(R re, I im) @safe pure nothrow
if (is(R : double) && is(I : double))
{
static if (isFloatingPoint!R || isFloatingPoint!I)
return Complex!(CommonType!(R, I))(re, im);
else
return Complex!double(re, im);
}
unittest
{
auto a = complex(1.0);
static assert (is(typeof(a) == Complex!double));
assert (a.re == 1.0);
assert (a.im == 0.0);
auto b = complex(2.0L);
static assert (is(typeof(b) == Complex!real));
assert (b.re == 2.0L);
assert (b.im == 0.0L);
auto c = complex(1.0, 2.0);
static assert (is(typeof(c) == Complex!double));
assert (c.re == 1.0);
assert (c.im == 2.0);
auto d = complex(3.0, 4.0L);
static assert (is(typeof(d) == Complex!real));
assert (d.re == 3.0);
assert (d.im == 4.0L);
auto e = complex(1);
static assert (is(typeof(e) == Complex!double));
assert (e.re == 1);
assert (e.im == 0);
auto f = complex(1L, 2);
static assert (is(typeof(f) == Complex!double));
assert (f.re == 1L);
assert (f.im == 2);
auto g = complex(3, 4.0L);
static assert (is(typeof(g) == Complex!real));
assert (g.re == 3);
assert (g.im == 4.0L);
}
/** A complex number parametrised by a type T. */
struct Complex(T) if (isFloatingPoint!T)
{

View file

@ -1212,7 +1212,7 @@ private
*/
void put( T val )
{
put( new Node( val ) );
appendNode( new Node( val ) );
m_count++;
}
@ -1224,7 +1224,8 @@ private
{
if( !rhs.empty )
{
put( rhs.m_first );
appendNode( rhs.m_first );
m_count++;
while( m_last.next !is null )
{
m_last = m_last.next;
@ -1261,6 +1262,8 @@ private
Node* todelete = n.next;
n.next = n.next.next;
//delete todelete;
assert( m_count > 0 );
m_count--;
}
@ -1280,6 +1283,7 @@ private
void clear()
{
m_first = m_last = null;
m_count = 0;
}
@ -1308,7 +1312,7 @@ private
/*
*
*/
void put( Node* n )
void appendNode( Node* n )
{
if( !empty )
{
@ -1358,15 +1362,24 @@ version( unittest )
prioritySend( tid, "done" );
}
unittest
void runTest( Tid tid )
{
auto tid = spawn( &testfn, thisTid );
send( tid, 42, 86 );
send( tid, tuple(42, 86) );
send( tid, "hello", "there" );
send( tid, "the quick brown fox" );
receive( (string val) { assert(val == "done"); } );
}
unittest
{
auto tid = spawn( &testfn, thisTid );
runTest( tid );
// Run the test again with a limited mailbox size.
tid = spawn( &testfn, thisTid );
setMaxMailboxSize( tid, 2, OnCrowding.block );
runTest( tid );
}
}

View file

@ -50,6 +50,15 @@ version(Windows)
enum string linesep = "\r\n"; /// String used to separate lines.
enum string curdir = "."; /// String representing the current directory.
enum string pardir = ".."; /// String representing the parent directory.
static assert(sep.length == 1 && altsep.length == 1);
private bool isSep(dchar ch) {
return ch == sep[0] || ch == altsep[0];
}
private bool isSepOrDriveSep(dchar ch) {
return isSep(ch) || ch == ':';
}
}
version(Posix)
{
@ -67,6 +76,11 @@ version(Posix)
enum string linesep = "\n";
enum string curdir = "."; /// String representing the current directory.
enum string pardir = ".."; /// String representing the parent directory.
static assert(sep.length == 1 && altsep.length == 0);
private bool isSep(dchar ch) {
return ch == sep[0];
}
}
/*****************************
@ -119,16 +133,16 @@ string getExt(string fullname)
while (i > 0)
{
if (fullname[i - 1] == '.')
return fullname[i .. fullname.length];
return fullname[i .. $];
i--;
version(Windows)
{
if (fullname[i] == ':' || fullname[i] == '\\')
if (isSepOrDriveSep(fullname[i]))
break;
}
else version(Posix)
{
if (fullname[i] == '/')
if (isSep(fullname[i]))
break;
}
else
@ -218,12 +232,12 @@ string getName(string fullname)
i--;
version(Windows)
{
if (fullname[i] == ':' || fullname[i] == '\\')
if (isSepOrDriveSep(fullname[i]))
break;
}
else version(Posix)
{
if (fullname[i] == '/')
if (isSep(fullname[i]))
break;
}
else
@ -297,12 +311,12 @@ body
{
version(Windows)
{
if (fullname[i - 1] == ':' || fullname[i - 1] == '\\' || fullname[i - 1] == '/')
if (isSepOrDriveSep(fullname[i - 1]))
break;
}
else version(Posix)
{
if (fullname[i - 1] == '/')
if (isSep(fullname[i - 1]))
break;
}
else
@ -310,7 +324,7 @@ body
static assert(0);
}
}
return chomp(fullname[i .. fullname.length],
return chomp(fullname[i .. $],
extension.length ? extension : "");
}
@ -661,7 +675,7 @@ string defaultExt(string filename, string ext)
if (existing.length == 0)
{
// Check for filename ending in '.'
if (filename.length && filename[filename.length - 1] == '.')
if (filename.length && filename[$ - 1] == '.')
filename ~= ext;
else
filename = filename ~ "." ~ ext;
@ -701,7 +715,7 @@ string addExt(string filename, string ext)
if (existing.length == 0)
{
// Check for filename ending in '.'
if (filename.length && filename[filename.length - 1] == '.')
if (filename.length && filename[$ - 1] == '.')
filename ~= ext;
else
filename = filename ~ "." ~ ext;
@ -744,12 +758,11 @@ bool isabs(in char[] path)
auto d = getDrive(path);
version (Windows)
{
return d.length < path.length &&
(path[d.length] == sep[0] || path[d.length] == altsep[0]);
return d.length < path.length && isSep(path[d.length]);
}
else version (Posix)
{
return d.length < path.length && path[d.length] == sep[0];
return d.length < path.length && isSep(path[d.length]);
}
else
{
@ -784,17 +797,17 @@ string rel2abs(string path)
return path;
}
auto myDir = getcwd;
if (path.startsWith(curdir[]))
if (path.startsWith(curdir))
{
auto p = path[curdir.length .. $];
if (p.startsWith(sep[]))
if (p.startsWith(sep))
path = p[sep.length .. $];
else if (altsep.length && p.startsWith(altsep[]))
else if (altsep.length && p.startsWith(altsep))
path = p[altsep.length .. $];
else if (!p.length)
path = null;
}
return myDir.endsWith(sep[]) || path.length
return myDir.endsWith(sep) || path.length
? join(myDir, path)
: myDir;
}
@ -857,7 +870,7 @@ string join(const(char)[] p1, const(char)[] p2, const(char)[][] more...)
version (Posix)
{
if (isabs(p2)) return p2.idup;
if (p1.endsWith(sep[]) || altsep.length && p1.endsWith(altsep[]))
if (p1.endsWith(sep) || altsep.length && p1.endsWith(altsep))
{
return cast(string) (p1 ~ p2);
}
@ -879,16 +892,16 @@ string join(const(char)[] p1, const(char)[] p2, const(char)[][] more...)
{
p = cast(string) (p1 ~ p2);
}
else if (p2[0] == '\\')
else if (isSep(p2[0]))
{
if (d1.length == 0)
p = p2.idup;
else if (p1[p1.length - 1] == '\\')
p = cast(string) (p1 ~ p2[1 .. p2.length]);
else if (isSep(p1[$ - 1]))
p = cast(string) (p1 ~ p2[1 .. $]);
else
p = cast(string) (p1 ~ p2);
}
else if (p1[p1.length - 1] == '\\')
else if (isSep(p1[$ - 1]))
{
p = cast(string) (p1 ~ p2);
}
@ -1107,8 +1120,8 @@ body
return true;
foreach (j; ni .. filename.length)
{
if (fnmatch(filename[j .. filename.length],
pattern[pi + 1 .. pattern.length]))
if (fnmatch(filename[j .. $],
pattern[pi + 1 .. $]))
return true;
}
return false;

View file

@ -1,11 +1,6 @@
// Written in the D programming language.
/**
Authors:
$(WEB digitalmars.com, Walter Bright), $(WEB erdani.org, Andrei
Alexandrescu)
Macros:
WIKI=Phobos/StdProcess
@ -311,10 +306,18 @@ else
} // version
}
/**
* Returns the process ID of the calling process, which is guaranteed to be
* unique on the system. This call is always successful.
*
* Example:
* ---
* writefln("Current process id: %s", getpid());
* ---
*/
version(Posix)
{
//alias std.c.process.getpid getpid;
import core.sys.posix.unistd : getpid;
alias core.sys.posix.unistd.getpid getpid;
}
else version (Windows)
{

View file

@ -29,7 +29,7 @@
/**
* Notes: For Win32 systems, link with ws2_32.lib.
* Example: See /dmd/samples/d/listener.d.
* Authors: Christopher E. Miller
* Authors: Christopher E. Miller, $(WEB klickverbot.at, David Nadlinger)
* Source: $(PHOBOSSRC std/_socket.d)
* Macros:
* WIKI=Phobos/StdSocket
@ -40,6 +40,11 @@ module std.socket;
import core.stdc.stdint, std.string, std.c.string, std.c.stdlib, std.conv,
std.traits;
import core.stdc.config;
import core.time : dur, Duration;
import std.algorithm : max;
import std.exception : assumeUnique, enforce;
version(unittest)
{
private import std.c.stdio : printf;
@ -74,9 +79,7 @@ else version(BsdSockets)
version(linux)
import std.c.linux.socket : AF_IPX, AF_APPLETALK, SOCK_RDM,
IPPROTO_IGMP, IPPROTO_GGP, IPPROTO_PUP, IPPROTO_IDP,
protoent, servent, hostent, SD_RECEIVE, SD_SEND, SD_BOTH,
MSG_NOSIGNAL, INADDR_NONE, getprotobyname, getprotobynumber,
getservbyname, getservbyport, gethostbyname, gethostbyaddr;
SD_RECEIVE, SD_SEND, SD_BOTH, MSG_NOSIGNAL, INADDR_NONE;
else version(OSX)
private import std.c.osx.socket;
else version(FreeBSD)
@ -90,6 +93,8 @@ else version(BsdSockets)
}
else
static assert(false);
import core.sys.posix.netdb;
private import core.sys.posix.fcntl;
private import core.sys.posix.unistd;
private import core.sys.posix.arpa.inet;
@ -176,6 +181,8 @@ class SocketException: Exception
}
private __gshared typeof(&getnameinfo) getnameinfoPointer;
shared static this()
{
version(Win32)
@ -188,6 +195,19 @@ shared static this()
val = WSAStartup(0x2020, &wd);
if(val) // Request Winsock 2.2 for IPv6.
throw new SocketException("Unable to initialize socket library", val);
// See the comment in InternetAddress.toHostNameString() for
// details on the getnameinfo() issue.
auto ws2Lib = GetModuleHandleA("ws2_32.dll");
if (ws2Lib)
{
getnameinfoPointer = cast(typeof(getnameinfoPointer))
GetProcAddress(ws2Lib, "getnameinfo");
}
}
else version(Posix)
{
getnameinfoPointer = &getnameinfo;
}
}
@ -564,6 +584,9 @@ class InternetHost
/**
* Resolve IPv4 address number. Returns false if unable to resolve.
*
* Params:
* addr = The IPv4 address to resolve, in network byte order.
*/
bool getHostByAddr(uint addr)
{
@ -828,6 +851,35 @@ class InternetAddress: Address
return std.conv.to!string(port());
}
/*
* Returns the host name as a fully qualified domain name, if
* available, or the IP address in dotted-decimal notation otherwise.
*/
string toHostNameString()
{
// getnameinfo() is the recommended way to perform a reverse (name)
// lookup on both Posix and Windows. However, it is only available
// on Windows XP and above, and not included with the WinSock import
// libraries shipped with DMD. Thus, we check for getnameinfo at
// runtime in the shared module constructor, and fall back to the
// deprecated getHostByAddr() if it could not be found. See also:
// http://technet.microsoft.com/en-us/library/aa450403.aspx
if (getnameinfoPointer is null)
{
auto host = new InternetHost();
enforce(host.getHostByAddr(sin.sin_addr.s_addr),
new SocketException("Could not get host name."));
return host.name;
}
auto buf = new char[NI_MAXHOST];
auto rc = getnameinfoPointer(cast(sockaddr*)&sin, sin.sizeof,
buf.ptr, cast(uint)buf.length, null, 0, 0);
enforce(rc == 0, new SocketException(
"Could not get host name", _lasterr()));
return assumeUnique(buf[0 .. strlen(buf.ptr)]);
}
/// Human readable string representing the IPv4 address and port in the form $(I a.b.c.d:e).
override string toString()
{
@ -896,8 +948,8 @@ enum SocketFlags: int
extern(C) struct timeval
{
// D interface
int seconds; /// Number of seconds.
int microseconds; /// Number of additional microseconds.
c_long seconds; /// Number of seconds.
c_long microseconds; /// Number of additional microseconds.
// C interface
deprecated
@ -1089,6 +1141,8 @@ enum SocketOption: int
SNDBUF = SO_SNDBUF, /// send buffer size
RCVBUF = SO_RCVBUF, /// receive buffer size
DONTROUTE = SO_DONTROUTE, /// do not route
SNDTIMEO = SO_SNDTIMEO, /// send timeout
RCVTIMEO = SO_RCVTIMEO, /// receive timeout
// SocketOptionLevel.TCP:
TCP_NODELAY = .TCP_NODELAY, /// disable the Nagle algorithm for send coalescing
@ -1115,6 +1169,12 @@ class Socket
version(Win32)
bool _blocking = false; /// Property to get or set whether the socket is blocking or nonblocking.
// The WinSock timeouts seem to be effectively skewed by a constant
// offset of about half a second (value in milliseconds). This has
// been confirmed on updated (as of Jun 2011) Windows XP, Windows 7
// and Windows Server 2008 R2 boxes.
enum WINSOCK_TIMEOUT_SKEW = 500;
// For use with accepting().
protected this()
@ -1562,6 +1622,31 @@ class Socket
return getOption(level, option, (&result)[0 .. 1]);
}
/// Get a timeout (duration) option.
void getOption(SocketOptionLevel level, SocketOption option, out Duration result)
{
enforce(option == SocketOption.SNDTIMEO || option == SocketOption.RCVTIMEO,
new SocketException("Not a valid timeout option: " ~ to!string(option)));
// WinSock returns the timeout values as a milliseconds DWORD,
// while Linux and BSD return a timeval struct.
version (Win32)
{
int msecs;
getOption(level, option, (&msecs)[0 .. 1]);
if (option == SocketOption.RCVTIMEO) {
msecs += WINSOCK_TIMEOUT_SKEW;
}
result = dur!"msecs"(msecs);
}
else version (BsdSockets)
{
timeval tv;
getOption(level, option, (&tv)[0..1]);
result = dur!"seconds"(tv.seconds) + dur!"usecs"(tv.microseconds);
}
else static assert(false);
}
// Set a socket option.
void setOption(SocketOptionLevel level, SocketOption option, void[] value)
{
@ -1585,6 +1670,74 @@ class Socket
setOption(level, option, (&value)[0 .. 1]);
}
/**
* Sets a timeout (duration) option, i.e. SocketOption.SNDTIMEO or
* RCVTIMEO. Zero indicates no timeout.
*
* In a typical application, you might also want to consider using
* a non-blocking socket instead of setting a timeout on a blocking one.
*
* Note: While the receive timeout setting is generally quite accurate
* on *nix systems even for smaller durations, there are two issues to
* be aware of on Windows: First, although undocumented, the effective
* timeout duration seems to be the one set on the socket plus half
* a second. setOption() tries to compensate for that, but still,
* timeouts under 500ms are not possible on Windows. Second, be aware
* that the actual amount of time spent until a blocking call returns
* randomly varies on the order of 10ms.
*
* Params:
* value = The timeout duration to set. Must not be negative.
*
* Throws: SocketException if setting the options fails.
*
* Example:
* ---
* import std.datetime;
* auto pair = socketPair();
* scope(exit) foreach (s; pair) s.close();
*
* // Set a receive timeout, and then wait at one end of
* // the socket pair, knowing that no data will arrive.
* pair[0].setOption(SocketOptionLevel.SOCKET,
* SocketOption.RCVTIMEO, dur!"seconds"(1));
*
* auto sw = StopWatch(AutoStart.yes);
* ubyte[1] buffer;
* pair[0].receive(buffer);
* writefln("Waited %s ms until the socket timed out.",
* sw.peek.msecs);
* ---
*/
void setOption(SocketOptionLevel level, SocketOption option, Duration value)
{
enforce(option == SocketOption.SNDTIMEO || option == SocketOption.RCVTIMEO,
new SocketException("Not a valid timeout option: " ~ to!string(option)));
enforce(value >= dur!"hnsecs"(0), new SocketException(
"Timeout duration must not be negative."));
version (Win32)
{
auto msecs = cast(int)value.total!"msecs"();
if (msecs == 0 || option != SocketOption.RCVTIMEO)
{
setOption(level, option, msecs);
}
else
{
setOption(level, option, cast(int)
max(1, msecs - WINSOCK_TIMEOUT_SKEW));
}
}
else version (BsdSockets)
{
timeval tv = { seconds: cast(int)value.total!"seconds"(),
microseconds: value.fracSec.usecs };
setOption(level, option, (&tv)[0 .. 1]);
}
else static assert(false);
}
/**
* Wait for a socket to change status. A wait timeout timeval or int microseconds may be specified; if a timeout is not specified or the timeval is null, the maximum timeout is used. The timeval timeout has an unspecified value when select returns. Returns the number of sockets with status changes, 0 on timeout, or -1 on interruption. If the return value is greater than 0, the SocketSets are updated to only contain the sockets having status changes. For a connecting socket, a write status change means the connection is established and it's able to send. For a listening socket, a read status change means there is an incoming connection request and it's able to accept.
@ -1760,6 +1913,19 @@ class UdpSocket: Socket
* The two sockets are indistinguishable.
*
* Throws: SocketException if creation of the sockets fails.
*
* Example:
* ---
* immutable ubyte[] data = [1, 2, 3, 4];
* auto pair = socketPair();
* scope(exit) foreach (s; pair) s.close();
*
* pair[0].send(data);
*
* auto buf = new ubyte[data.length];
* pair[1].receive(buf);
* assert(buf == data);
* ---
*/
Socket[2] socketPair() {
version(BsdSockets) {
@ -1795,15 +1961,13 @@ Socket[2] socketPair() {
}
unittest {
ubyte[] data = [1, 2, 3, 4];
immutable ubyte[] data = [1, 2, 3, 4];
auto pair = socketPair();
scope(exit) foreach (s; pair) s.close();
pair[0].send(data);
ubyte[] buf = new ubyte[data.length];
auto buf = new ubyte[data.length];
pair[1].receive(buf);
assert(buf == data);
pair[0].close();
pair[1].close();
}