├── Makefile ├── Packets.h ├── README.md ├── context.c ├── internal.h ├── loop.c ├── server.c ├── socket.c ├── stress.c ├── tcp.pro ├── test.c └── uthash.h /Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | rm -f *.o 3 | $(CC) $(CFLAGS) -g -c loop.c context.c socket.c 4 | $(AR) rvs uSockets_userspace.a *.o 5 | $(CC) stress.c uSockets_userspace.a -o stress 6 | $(CC) server.c uSockets_userspace.a -o server 7 | -------------------------------------------------------------------------------- /Packets.h: -------------------------------------------------------------------------------- 1 | #ifndef PACKETS_H 2 | #define PACKETS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #define IpHeader struct iphdr 22 | 23 | static inline int IpHeader_getVersion(IpHeader *ip) { 24 | return ip->version; 25 | } 26 | 27 | static inline int IpHeader_getHeaderLength(IpHeader *ip) { 28 | return ip->ihl * 4; 29 | } 30 | 31 | static inline void *IpHeader_getData(IpHeader *ip) { 32 | return ((char *) ip) + IpHeader_getHeaderLength(ip); 33 | } 34 | 35 | // fel! 36 | static inline int IpHeader_getTotalLength(IpHeader *ip) { 37 | return ntohs(ip->tot_len); 38 | } 39 | 40 | static inline int IpHeader_getFragmentOffset(IpHeader *ip) { 41 | return ntohs(ip->frag_off); 42 | } 43 | 44 | //#define TcpHeader struct tcphdr 45 | 46 | struct TcpHeader { 47 | struct tcphdr header; 48 | 49 | unsigned char options[4]; 50 | }; 51 | 52 | #include 53 | 54 | static inline uint16_t TcpHeader_getDestinationPort(struct TcpHeader *tcp) { 55 | return ntohs(tcp->header.dest); 56 | } 57 | 58 | static inline uint16_t TcpHeader_getSourcePort(struct TcpHeader *tcp) { 59 | return ntohs(tcp->header.source); 60 | } 61 | 62 | #endif // PACKETS_H 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## experiment 2 | 3 | experimental uSockets implementation 4 | 5 | ##### Disabling the kernel speace TCP stack for a certain port 6 | You will need to disable RST outputting on ports you listen to. Disable RST on port 4000 like so: 7 | 8 | `sudo iptables -A OUTPUT -p tcp --sport 4000 --tcp-flags RST RST -j DROP` 9 | -------------------------------------------------------------------------------- /context.c: -------------------------------------------------------------------------------- 1 | // socket context implements tcp protcol 2 | 3 | /* uSockets is entierly opaque so we can use the real header straight up */ 4 | #include "../uWebSockets.js/uWebSockets/uSockets/src/libusockets.h" 5 | 6 | #include "internal.h" 7 | 8 | /* Static timer info */ 9 | struct us_internal_socket_timer_t { 10 | struct us_socket_t *s; 11 | int ticks; 12 | /* For now we have a callback */ 13 | void (*cb)(struct us_socket_t *); 14 | }; 15 | 16 | // per loop, per application 17 | struct us_internal_socket_timer_t *timers = 0; 18 | int num_timers = 1000000; 19 | 20 | void us_internal_socket_timeout(struct us_internal_socket_timer_t *timer); 21 | 22 | 23 | /* Timer logic could be separate module */ 24 | void us_internal_small_tick() { 25 | 26 | /* Init timers */ 27 | if (timers == 0) { 28 | timers = calloc(1000000, sizeof(struct us_internal_socket_timer_t)); 29 | } 30 | 31 | /* Sweep them up to num_timers */ 32 | for (int i = 0; i < num_timers; i++) { 33 | /* Is this a gap? */ 34 | if (timers[i].s && timers[i].ticks) { 35 | if (--timers[i].ticks == 0) { 36 | /* Timer has triggered (this may rearm the timer but only itself no other socket?) */ 37 | us_internal_socket_timeout(&timers[i]); 38 | } 39 | } 40 | } 41 | 42 | } 43 | 44 | /* Arms or resets a timer based on increments of 250ms. 45 | Settings to 0 disables the timer. */ 46 | void us_internal_add_timeout(int ticks, struct us_socket_t *s) { 47 | 48 | /* Sweep from start to end until a gap is there */ 49 | for (int i = 0; i < 1000000; i++) { 50 | /* This slot is expired */ 51 | if (!timers[i].ticks) { 52 | timers[i].ticks = ticks; 53 | timers[i].s = s; 54 | break; 55 | } 56 | } 57 | } 58 | 59 | void us_internal_remove_timeout(struct us_socket_t *s) { 60 | for (int i = 0; i < 1000000; i++) { 61 | /* This slot is expired */ 62 | if (timers[i].s == s) { 63 | timers[i].ticks = 0; 64 | timers[i].s = 0; 65 | break; 66 | } 67 | } 68 | } 69 | 70 | // socket container with lookup 71 | 72 | #define SOCKET_SYN_ACK_SENT 1 73 | #define SOCKET_ESTABLISHED 2 74 | #define SOCKET_SYN_SENT 3 75 | 76 | /* Socket timeout handler cannot call us_internal_set_timeot but can access the timer directly */ 77 | void us_internal_socket_timeout(struct us_internal_socket_timer_t *timer) { 78 | //printf("Socket timed out!\n"); 79 | 80 | struct us_socket_t *s = timer->s; 81 | 82 | if (timer->s->state == SOCKET_SYN_ACK_SENT) { 83 | printf("RETRANSMITTING SYN,ACK!\n"); 84 | 85 | if (s->initialHostSeq != s->hostSeq) { 86 | printf("RETRANSMITTING WRONG? %d vs. %d\n", s->initialHostSeq, s->hostSeq); 87 | } 88 | 89 | if (s->initialRemoteSeq != s->hostAck) { 90 | printf("RETRANSMITTING WRONG (ack)? %d vs. %d\n", s->initialRemoteSeq, s->hostAck); 91 | } 92 | 93 | /* Send syn, ack */ 94 | us_internal_socket_context_send_packet(s->context, s->hostSeq, s->hostAck + 1, s->networkIp, s->networkDestinationIp, s->hostPort, s->hostDestinationPort, 1, 1, 0, 0, NULL, 0); 95 | 96 | /* Remove this timer */ 97 | //timer->s = 0; 98 | 99 | /* Rearm this timer */ 100 | timer->ticks = 2; 101 | } 102 | } 103 | 104 | static unsigned long getPseudoHeaderSum(u_int32_t saddr, u_int32_t daddr, u_int16_t tcpLength) { 105 | struct PseudoHeader { 106 | u_int32_t source_address; 107 | u_int32_t dest_address; 108 | u_int8_t placeholder; 109 | u_int8_t protocol; 110 | u_int16_t tcp_length; 111 | } volatile pseudoHeader = {saddr, daddr, 0, IPPROTO_TCP, tcpLength}; 112 | 113 | unsigned short *ptr = (unsigned short *) &pseudoHeader; 114 | unsigned long sum = 0; 115 | for (int i = 0; i < 6; i++) { 116 | sum += *ptr++; 117 | } 118 | return sum; 119 | } 120 | 121 | static unsigned short csum_continue(unsigned long sumStart, char *p,int nbytes) 122 | { 123 | unsigned short *ptr = (unsigned short *) p; 124 | 125 | register long sum; 126 | unsigned short oddbyte; 127 | register short answer; 128 | 129 | sum=sumStart; 130 | while(nbytes>1) { 131 | sum+=*ptr++; 132 | nbytes-=2; 133 | } 134 | if(nbytes==1) { 135 | oddbyte=0; 136 | *((u_char*)&oddbyte)=*(u_char*)ptr; 137 | sum+=oddbyte; 138 | } 139 | 140 | sum = (sum>>16)+(sum & 0xffff); 141 | sum = sum + (sum>>16); 142 | answer=(short)~sum; 143 | 144 | return(answer); 145 | } 146 | 147 | IpHeader *getIpPacketBuffer(struct us_loop_t *loop); 148 | 149 | // needs to know of the loop (IP) 150 | void us_internal_socket_context_send_packet(struct us_socket_context_t *context, uint32_t hostSeq, uint32_t hostAck, uint32_t networkDestIp, uint32_t networkSourceIp, int hostDestPort, 151 | int hostSourcePort, int flagAck, int flagSyn, int flagFin, int flagRst, char *data, size_t length) { 152 | 153 | if (rand() % 10 == 1) { 154 | printf("Dropping packet now!\n"); 155 | return; 156 | } 157 | 158 | // for testing: send RST when sending FIN 159 | // the iptables rule filters out the packet when RST flag is set! 160 | if (flagFin) { 161 | //flagRst = 1; 162 | } 163 | 164 | 165 | struct us_loop_t *loop = context->loop; 166 | 167 | IpHeader *ipHeader = getIpPacketBuffer(loop); 168 | 169 | memset(ipHeader, 0, sizeof(struct iphdr)); 170 | 171 | ipHeader->ihl = 5; 172 | ipHeader->version = 4; 173 | ipHeader->tot_len = htons(sizeof(struct iphdr) + sizeof(struct TcpHeader) + length); 174 | ipHeader->id = htonl(54321); 175 | ipHeader->ttl = 255; 176 | ipHeader->protocol = IPPROTO_TCP; 177 | ipHeader->saddr = networkSourceIp; 178 | ipHeader->daddr = networkDestIp; 179 | //ipHeader->check = csum_continue(0, (char *) ipHeader, sizeof(iphdr)); 180 | 181 | struct TcpHeader *tcpHeader = (struct TcpHeader *) IpHeader_getData(ipHeader);//ipHeader->getData(); 182 | memset(tcpHeader, 0, sizeof(struct TcpHeader)); 183 | 184 | tcpHeader->header.ack = flagAck; 185 | tcpHeader->header.syn = flagSyn; 186 | tcpHeader->header.fin = flagFin; 187 | tcpHeader->header.rst = flagRst; 188 | if (data) { 189 | tcpHeader->header.psh = 1; 190 | memcpy(((char *) tcpHeader) + sizeof(struct TcpHeader), data, length); 191 | } 192 | 193 | tcpHeader->header.ack_seq = htonl(hostAck); 194 | tcpHeader->header.seq = htonl(hostSeq); 195 | tcpHeader->header.source = htons(hostSourcePort); 196 | tcpHeader->header.dest = htons(hostDestPort); 197 | 198 | // window scale 512kb / 2 199 | tcpHeader->options[0] = 3; 200 | tcpHeader->options[1] = 3; 201 | tcpHeader->options[2] = 5; // shift 202 | tcpHeader->options[3] = 0; 203 | 204 | // todo 205 | tcpHeader->header.doff = 6; // 5 * 4 = 20 bytes 206 | tcpHeader->header.window = htons(8192); 207 | 208 | tcpHeader->header.check = csum_continue(getPseudoHeaderSum(networkSourceIp, networkDestIp, htons(sizeof(struct TcpHeader) + length)) 209 | , (char *) tcpHeader, sizeof(struct TcpHeader) + length); 210 | 211 | printf("%fs [Outgoing ", 100.0f * (float) clock() / (float) CLOCKS_PER_SEC); 212 | print_packet(tcpHeader); 213 | } 214 | 215 | // this is the only socket we can keep for now 216 | struct us_socket_t *global_s; 217 | 218 | // looking up a socket from its properties 219 | struct us_socket_t *lookup_socket(uint32_t sourceIP, uint16_t sourcePort, uint32_t destIP, uint16_t destPort) { 220 | 221 | 222 | return global_s; 223 | } 224 | 225 | // creates a new socket with the given description 226 | struct us_socket_t *add_socket() { 227 | if (global_s) { 228 | printf("ERRO! ALREADY HAVING ONE SOCKET!\n"); 229 | exit(1); 230 | } 231 | 232 | // allokera mer! 233 | global_s = malloc(sizeof(struct us_socket_t) + /*256*/512); 234 | // init the socket 235 | 236 | global_s->closed = 0; 237 | global_s->shutdown = 0; 238 | 239 | global_s->prev = 0; 240 | global_s->next = 0; 241 | 242 | return global_s; 243 | } 244 | 245 | void remove_socket(struct us_socket_t *s); 246 | 247 | //us_socket_write can take identifier to merge common sends into one 248 | 249 | print_packet(struct TcpHeader *tcpHeader) { 250 | printf("packet, seq %u, ack_seq: %u", ntohl(tcpHeader->header.seq), ntohl(tcpHeader->header.ack_seq)); 251 | /* Får vi någonsin RST? */ 252 | if (tcpHeader->header.rst) { 253 | printf(", RST"); 254 | } 255 | if (tcpHeader->header.syn) { 256 | printf(", SYN"); 257 | } 258 | if (tcpHeader->header.ack) { 259 | printf(", ACK"); 260 | } 261 | if (tcpHeader->header.fin) { 262 | printf(", FIN"); 263 | } 264 | /*if (tcpHeader->header.rst) { 265 | printf("RST, "); 266 | }*/ 267 | printf("]\n"); 268 | } 269 | 270 | 271 | // pass tcp data to the context - call it read_packet, send_packet 272 | void us_internal_socket_context_read_tcp(struct us_socket_t *s, struct us_socket_context_t *context, IpHeader *ipHeader, struct TcpHeader *tcpHeader, int length) { 273 | 274 | /* Always debug the incoming packet in terms of flags and syn, syn_ack */ 275 | printf("%fs [Incoming ", 100.0f * (float) clock() / (float) CLOCKS_PER_SEC); 276 | print_packet(tcpHeader); 277 | 278 | /* Drop packets 10% */ 279 | if (rand() % 10 == 1) { 280 | printf("Dropping packet now!\n"); 281 | return; 282 | } 283 | 284 | if (!s) { 285 | /* Is this a SYN but not an ACK? */ 286 | if (tcpHeader->header.syn && !tcpHeader->header.ack) { 287 | /* Allocate the socket */ 288 | uint32_t hostAck = ntohl(tcpHeader->header.seq); 289 | uint32_t hostSeq = rand(); 290 | 291 | struct us_socket_t *s = add_socket(); 292 | 293 | // store initial numbers for debugging when shit happens 294 | s->initialRemoteSeq = hostAck; 295 | s->initialHostSeq = hostSeq; 296 | 297 | s->state = SOCKET_SYN_ACK_SENT; 298 | s->context = context; 299 | s->hostAck = hostAck; 300 | s->hostSeq = hostSeq; 301 | 302 | /* If we are about to overflow */ 303 | if (s->hostAck > UINT32_MAX - 100) { 304 | printf("WE ARE GOING TO FLIP SOON!\n"); 305 | exit(0); 306 | } 307 | 308 | s->networkIp = ipHeader->saddr; 309 | s->hostPort = TcpHeader_getSourcePort(tcpHeader); 310 | 311 | s->networkDestinationIp = ipHeader->daddr; 312 | s->hostDestinationPort = TcpHeader_getDestinationPort(tcpHeader); 313 | 314 | /* Debug */ 315 | s->packets = 1; // obviously we got SYN 316 | s->mostOutOfSync = 0; 317 | 318 | // what was the initial hostAck of this socket? how many times did we retransmit? what is it now? 319 | // did we retransmit wrong? 320 | 321 | /* Test: We do not respond here, but in the timeout instead (we simulate dropping the SynAck) */ 322 | 323 | /* Send syn, ack */ 324 | us_internal_socket_context_send_packet(s->context, s->hostSeq, s->hostAck + 1, s->networkIp, s->networkDestinationIp, s->hostPort, s->hostDestinationPort, 1, 1, 0, 0, NULL, 0); 325 | 326 | /* We require an ACK within 500 ms, or we retransmit */ 327 | us_internal_add_timeout(2, s); 328 | 329 | /* Now we will return, and global_s is added to the hash table in loop */ 330 | } else { 331 | /* All other packtes in this state are uninvited */ 332 | printf("Dropping uninvited packet\n"); 333 | } 334 | } else { 335 | 336 | // what if we get an extra SYN here? 337 | if (tcpHeader->header.syn) { 338 | printf("GOT DUPLICATE SYN!\n"); 339 | return; 340 | } 341 | 342 | // öka paket för statistik 343 | context->loop->packets_received++; 344 | 345 | // SYN allokerar socketen, alla nästkommande paket räknas upp 346 | s->packets++; 347 | 348 | /*if (s->state == SOCKET_SYN_ACK_SENT) { 349 | // if we get a SYN resent in this state, we know we lost it 350 | if (tcpHeader->header.syn && !tcpHeader->header.ack) { 351 | printf("WE WERE RESENT A SYN WHILE ALREADY IN SYN-ACK-SENT STATE!\n"); 352 | //us_internal_socket_context_read_tcp() 353 | return; 354 | } 355 | }*/ 356 | 357 | 358 | if (tcpHeader->header.fin) { 359 | 360 | printf("We got fin!\n"); 361 | 362 | // emit callback 363 | context->on_close(s); 364 | 365 | // send fin, ack back 366 | s->hostAck += 1; 367 | 368 | us_internal_socket_context_send_packet(context, s->hostSeq, s->hostAck, ipHeader->saddr, ipHeader->daddr, ntohs(tcpHeader->header.source), ntohs(tcpHeader->header.dest), 1, 0, 1, 0, NULL, 0); 369 | 370 | printf("Deleting socket now!\n"); 371 | remove_socket(s); 372 | 373 | /* Cannot fall through to data when deleted */ 374 | return; 375 | 376 | } else if (tcpHeader->header.ack) { 377 | 378 | if (s->state == SOCKET_ESTABLISHED) { 379 | // why thank you 380 | } else if (s->state == SOCKET_SYN_ACK_SENT) { 381 | 382 | // if our syn-ack is lost, we believe we sent it but we haven't 383 | 384 | /* Here we are established, and we may also get data */ 385 | uint32_t seq = ntohl(tcpHeader->header.seq); 386 | uint32_t ack = ntohl(tcpHeader->header.ack_seq); 387 | 388 | if (s->hostAck + 1 == seq && s->hostSeq + 1 == ack) { 389 | s->hostAck++; 390 | s->hostSeq++; 391 | s->state = SOCKET_ESTABLISHED; 392 | 393 | /* We are now established, remove timeout for socket */ 394 | us_internal_remove_timeout(s); 395 | 396 | /* link it with this context for sweeps */ 397 | us_internal_socket_context_link(context, s); 398 | 399 | /* Emit open event */ 400 | context->on_open(s, 0, "nej!", 0); 401 | 402 | static int sockets; 403 | sockets++; 404 | 405 | if (sockets % 1000 == 0) 406 | printf("Sockets open: %d\n", sockets); 407 | 408 | } else { 409 | 410 | // vi får fel ack från clienten, förmodligen skickar vi fel seq i retransmit - jämför mot initial? 411 | 412 | // dessa stämmer 413 | printf("Expected: %d got: %d\n", s->hostAck + 1, seq); 414 | 415 | // dessa stämmer inte? 416 | printf("Expected: %d got: %d or got: %d\n", s->hostSeq + 1, ack, ntohl(ack)); 417 | 418 | printf("Initial numbers: %d, %d\n", s->initialHostSeq, s->initialRemoteSeq); 419 | 420 | // vi kommer hit utan data nu? skickar vi till oss själva? 421 | 422 | if (tcpHeader->header.syn) { 423 | 424 | printf("VI TOG EMOT ETT SYN,ACK!???\n"); 425 | } 426 | 427 | if (tcpHeader->header.rst) { 428 | 429 | printf("Tog vi emot ett RST?\n"); 430 | } 431 | 432 | // what if we lost the SYN -> SYN_ACK -> [ACK] from the client, followed by next packet which is ACK with data? 433 | 434 | int tcpdatalen = ntohs(ipHeader->tot_len) - (tcpHeader->header.doff * 4) - (ipHeader->ihl * 4); 435 | 436 | printf("Server ack is wrong, and we got data? of length: %d\n", tcpdatalen); 437 | exit(0); 438 | } 439 | 440 | } else if (tcpHeader->header.syn && s->state == SOCKET_SYN_SENT) { 441 | 442 | // this is for outbound connections 443 | 444 | printf("Socket syn sent!\n"); 445 | 446 | uint32_t ack = ntohl(tcpHeader->header.ack_seq); 447 | 448 | if (s->hostSeq + 1 == ack) { 449 | 450 | uint32_t seq = ntohl(tcpHeader->header.seq); 451 | s->hostSeq++; 452 | s->hostAck = seq + 1; 453 | s->state = SOCKET_ESTABLISHED; 454 | 455 | s->originalHostAck = s->hostAck; 456 | 457 | us_internal_socket_context_send_packet(context, s->hostSeq, s->hostAck, ipHeader->saddr, ipHeader->daddr, ntohs(tcpHeader->header.source), ntohs(tcpHeader->header.dest), 1, 0, 0, 0, NULL, 0); 458 | 459 | context->on_open(s, 0, 0, 0); 460 | 461 | return; 462 | 463 | // can data be ready to dispatch here? Don't think so? 464 | } else { 465 | printf("client ack is wrong!\n"); 466 | } 467 | } 468 | } 469 | 470 | /* Data handler runs for everything falling through from above */ 471 | int tcpdatalen = ntohs(ipHeader->tot_len) - (tcpHeader->header.doff * 4) - (ipHeader->ihl * 4); 472 | if (tcpdatalen) { 473 | char *buf = (char *) ipHeader; 474 | 475 | // how big can an IP packet be? 476 | if (buf + tcpdatalen - (char *) ipHeader > length) { 477 | printf("ERROR! length mismatch!\n"); 478 | exit(0); 479 | //std::cout << "ERROR: lengths mismatch!" << std::endl; 480 | //std::cout << "tcpdatalen: " << tcpdatalen << std::endl; 481 | //std::cout << "ip total length: " << ipHeader->getTotalLength() << std::endl; 482 | //std::cout << "buffer length: " << length << std::endl; 483 | //exit(-1); 484 | } 485 | 486 | /* Is this segment out of sequence? */ 487 | uint32_t seq = ntohl(tcpHeader->header.seq); 488 | 489 | /* Simple check: if the difference between the two is very large, then overflow error? */ 490 | //if () 491 | 492 | 493 | if (s->hostAck != seq) { 494 | 495 | /* This implementation should rarely see out of orders, since most sends are 1 packet big */ 496 | /* We only throw away future packets, and count on TCP resending them. Duplicates are acked again */ 497 | 498 | /* We have already seen this packet, calm down, the client did not get ack in time */ 499 | if (s->hostAck > seq) { 500 | 501 | context->loop->duplicated_packets++; 502 | 503 | /* Should probably send a new ack */ 504 | us_internal_socket_context_send_packet(context, s->hostSeq, s->hostAck, ipHeader->saddr, ipHeader->daddr, ntohs(tcpHeader->header.source), ntohs(tcpHeader->header.dest), 1, 0, 0, 0, NULL, 0); 505 | 506 | return; 507 | 508 | } else { 509 | /* We got a packet that is supposed to be for the future, we need to buffer it up or just throw it away */ 510 | uint32_t future = (seq - s->hostAck); 511 | //printf("We got a packet that is %d bytes in the future as socket's %d packet so far including SYN\n", future, s->packets); 512 | 513 | 514 | // increase for statistics 515 | context->loop->packets_out_of_order++; 516 | 517 | // for now just store one 518 | if (!s->mostOutOfSync) { 519 | s->mostOutOfSync = seq; 520 | } 521 | 522 | 523 | // om en socket som är i fucked state får ett nytt paket som är i fas 524 | 525 | 526 | // we should mark this socket with the highest seen packet, then tell the user when we "healed" from this out of order 527 | 528 | /* For now we just throw this packet to hell */ 529 | return; 530 | 531 | /* This should be buffered up and released when the missing piece reached us */ 532 | exit(0); 533 | } 534 | } else { 535 | 536 | /* If we are about to overflow */ 537 | if (s->hostAck > UINT32_MAX - 100) { 538 | printf("WE ARE GOING TO FLIP SOON!\n"); 539 | exit(0); 540 | } 541 | 542 | /* Increase by length of data */ 543 | s->hostAck += tcpdatalen; 544 | uint32_t lastHostSeq = s->hostSeq; 545 | 546 | /* Now that we received data in sync, did we heal the out of sync? */ 547 | if (s->mostOutOfSync) { 548 | printf("WE HELATED A SOCKET A BIT!\n"); 549 | context->loop->healed_sockets++; 550 | s->mostOutOfSync = 0; 551 | } 552 | 553 | /* Emit data, changing socket if necessary */ 554 | s = context->on_data(s, buf + ipHeader->ihl * 4 + tcpHeader->header.doff * 4, tcpdatalen); 555 | 556 | /* If we did not send anything in the callback, send and ack (we're not piggybacking) */ 557 | if (lastHostSeq == s->hostSeq) { 558 | us_internal_socket_context_send_packet(context, s->hostSeq, s->hostAck, ipHeader->saddr, ipHeader->daddr, ntohs(tcpHeader->header.source), ntohs(tcpHeader->header.dest), 1, 0, 0, 0, NULL, 0); 559 | } 560 | } 561 | } else { 562 | /* We got something here, ack, or something we don't care about */ 563 | } 564 | } 565 | } 566 | 567 | void us_internal_socket_context_link(struct us_socket_context_t *context, struct us_socket_t *s) { 568 | s->context = context; 569 | s->timeout = 0; 570 | s->next = context->head; 571 | s->prev = 0; 572 | if (context->head) { 573 | context->head->prev = s; 574 | } 575 | context->head = s; 576 | } 577 | 578 | struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *loop, int ext_size, struct us_socket_context_options_t options) { 579 | struct us_socket_context_t *socket_context = (struct us_socket_context_t *) malloc(sizeof(struct us_socket_context_t) + ext_size); 580 | 581 | /* Link socket context to loop */ 582 | socket_context->loop = loop; 583 | socket_context->listen_socket = 0; 584 | 585 | /* Link loop to socket contexts */ 586 | socket_context->next = loop->context; 587 | loop->context = socket_context; 588 | 589 | socket_context->head = 0; 590 | socket_context->iterator = 0; 591 | socket_context->prev = 0; 592 | 593 | us_internal_loop_link(loop, socket_context); 594 | 595 | return socket_context; 596 | } 597 | 598 | void us_socket_context_free(int ssl, struct us_socket_context_t *context) { 599 | free(context); 600 | } 601 | 602 | void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)) { 603 | context->on_open = on_open; 604 | } 605 | 606 | void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_close)(struct us_socket_t *s)) { 607 | context->on_close = on_close; 608 | } 609 | 610 | void us_socket_context_on_data(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_data)(struct us_socket_t *s, char *data, int length)) { 611 | context->on_data = on_data; 612 | } 613 | 614 | void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_writable)(struct us_socket_t *s)) { 615 | context->on_writable = on_writable; 616 | } 617 | 618 | void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_timeout)(struct us_socket_t *s)) { 619 | context->on_socket_timeout = on_timeout; 620 | } 621 | 622 | void us_socket_context_on_end(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_end)(struct us_socket_t *s)) { 623 | context->on_end = on_end; 624 | } 625 | 626 | void *us_socket_context_ext(int ssl, struct us_socket_context_t *context) { 627 | return context + 1; 628 | } 629 | 630 | struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size) { 631 | struct us_listen_socket_t *listen_socket = (struct us_listen_socket_t *) malloc(sizeof(struct us_listen_socket_t)); 632 | 633 | listen_socket->socket_ext_size = socket_ext_size; 634 | listen_socket->context = context; 635 | 636 | printf("Overriding listen port to 4000!\n"); 637 | port = 4000; 638 | 639 | /* What do you listen to? */ 640 | listen_socket->port = port; 641 | 642 | /* Context holds a list of listen sockets */ 643 | context->listen_socket = listen_socket; 644 | 645 | return listen_socket; 646 | } 647 | 648 | void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) { 649 | free(ls); 650 | } 651 | 652 | struct NetworkAddress { 653 | uint32_t addr; 654 | uint16_t port; 655 | }; 656 | 657 | // networkAddress, hostPort 658 | struct NetworkAddress networkAddressFromString(char *address) { 659 | unsigned int addr[5]; 660 | sscanf(address, "%d.%d.%d.%d:%d", &addr[0], &addr[1], &addr[2], &addr[3], &addr[4]); 661 | 662 | uint32_t networkAddress = addr[0] << 24 | addr[1] << 16 | addr[2] << 8 | addr[3]; 663 | struct NetworkAddress na = {htonl(networkAddress), addr[4]}; 664 | 665 | return na; 666 | } 667 | 668 | /*void Context::connect(char *source, char *destination, void *userData) 669 | { 670 | // these should return an Endpoint straight up 671 | struct NetworkAddress sourceAddress = networkAddressFromString(source); 672 | struct NetworkAddress destinationAddress = networkAddressFromString(destination); 673 | 674 | Endpoint endpoint = {destinationAddress.first, destinationAddress.second, sourceAddress.first, sourceAddress.second}; 675 | 676 | uint32_t hostSeq = rand(); 677 | sockets[endpoint] = new Socket({nullptr, destinationAddress.first, destinationAddress.second, sourceAddress.first, sourceAddress.second, 0, hostSeq, Socket::SYN_SENT}); 678 | 679 | Socket::sendPacket(hostSeq, 0, destinationAddress.first, sourceAddress.first, destinationAddress.second, sourceAddress.second, false, true, false, false, nullptr, 0); 680 | ip->releasePackageBatch(); 681 | }*/ 682 | 683 | // connect sends a syn but ends up in similar path as the rest pretty quickly 684 | struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, const char *interface, int options, int socket_ext_size) { 685 | printf("us_socket_context_connect\n"); 686 | 687 | // these should return an Endpoint straight up 688 | struct NetworkAddress sourceAddress = networkAddressFromString("127.0.0.1:45000"); 689 | struct NetworkAddress destinationAddress = networkAddressFromString("127.0.0.1:4000"); 690 | 691 | // we use 127.0.0.1 as our ip always, and we pick ephemeral port from a list 692 | 693 | //uint32_t clientIP = 123123123; 694 | //uint16_t clientPort = 12330; 695 | 696 | /* Allocate the socket */ 697 | //uint32_t hostAck = ntohl(tcpHeader->header.seq); 698 | /*uint32_t hostSeq = rand(); 699 | 700 | struct us_socket_t *s = add_socket(); 701 | 702 | s->state = SOCKET_SYN_ACK_SENT; 703 | s->context = context; 704 | s->hostAck = hostAck; 705 | s->hostSeq = hostSeq; 706 | 707 | s->networkIp = ipHeader->saddr; 708 | s->hostPort = TcpHeader_getSourcePort(tcpHeader); 709 | 710 | s->networkDestinationIp = ipHeader->daddr; 711 | s->hostDestinationPort = TcpHeader_getDestinationPort(tcpHeader); 712 | 713 | /* Debug */ 714 | //s->packets = 1; // obviously we got SYN 715 | //s->mostOutOfSync = 0; 716 | 717 | /* Send syn, ack */ 718 | //us_internal_socket_context_send_packet(context, hostSeq, 0, destinationAddress.addr, ipHeader->daddr, ntohs(tcpHeader->header.source), ntohs(tcpHeader->header.dest), 1, 1, 0, 0, NULL, 0); 719 | 720 | 721 | 722 | return 0; 723 | } 724 | 725 | struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context) { 726 | return context->loop; 727 | } 728 | 729 | struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size) { 730 | 731 | // FOR NOW WE DO NOT SUPPORT CHANGING SOCKET, SINCE WE DO NOT SUPPORT UPDATING THE HASH TABLE YET! 732 | 733 | //struct us_socket_t *new_s = (struct us_socket_t *) realloc(s, sizeof(struct us_socket_t) + ext_size); 734 | /*new_*/s->context = context; 735 | 736 | return /*new_*/s; 737 | } 738 | 739 | struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size) { 740 | /* We simply create a new context in this mock */ 741 | struct us_socket_context_options_t options = {}; 742 | struct us_socket_context_t *child_context = us_create_socket_context(ssl, context->loop, context_ext_size, options); 743 | 744 | return child_context; 745 | } 746 | -------------------------------------------------------------------------------- /internal.h: -------------------------------------------------------------------------------- 1 | #ifndef INTERNAL_H 2 | #define INTERNAL_H 3 | 4 | #define _GNU_SOURCE 5 | 6 | #define _GNU_SOURCE /* See feature_test_macros(7) */ 7 | #include 8 | 9 | #include "Packets.h" 10 | 11 | void us_internal_socket_context_link(struct us_socket_context_t *context, struct us_socket_t *s); 12 | void us_internal_timer_sweep(struct us_loop_t *loop); 13 | void us_internal_loop_link(struct us_loop_t *loop, struct us_socket_context_t *context); 14 | 15 | struct us_socket_context_t { 16 | alignas(16) struct us_loop_t *loop; 17 | 18 | struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length); 19 | struct us_socket_t *(*on_close)(struct us_socket_t *s); 20 | struct us_socket_t *(*on_data)(struct us_socket_t *s, char *data, int length); 21 | struct us_socket_t *(*on_writable)(struct us_socket_t *s); 22 | struct us_socket_t *(*on_socket_timeout)(struct us_socket_t *s); 23 | struct us_socket_t *(*on_end)(struct us_socket_t *s); 24 | 25 | /* Socket contexts form a link */ 26 | struct us_socket_context_t *next; 27 | struct us_socket_context_t *prev; 28 | 29 | /* What are our listen_sockets */ 30 | struct us_listen_socket_t *listen_socket; 31 | 32 | 33 | struct us_socket_t *head; 34 | struct us_socket_t *iterator; 35 | }; 36 | 37 | struct us_listen_socket_t { 38 | int socket_ext_size; 39 | struct us_socket_context_t *context; 40 | 41 | /* What do we really listen to? */ 42 | int port; 43 | }; 44 | 45 | #include "uthash.h" 46 | 47 | // 96 bit, 12 bytes (for now duplicated) 48 | struct SOCKET_KEY { 49 | uint16_t srcPort, dstPort; 50 | uint32_t srcIp, dstIp; 51 | }; 52 | 53 | struct us_socket_t { 54 | 55 | struct SOCKET_KEY key; 56 | 57 | // debug 58 | int packets; 59 | 60 | // largest seq we seen that is out of sync, or 0 if not seen 61 | uint32_t mostOutOfSync; 62 | 63 | // the initial seq, ack sent in first syn packet to us 64 | uint32_t initialRemoteSeq, initialHostSeq; 65 | 66 | 67 | //struct us_socket_t *next; 68 | 69 | void *userData; 70 | 71 | // per socket data 72 | uint32_t networkIp; // this is THEIR IP! 73 | uint16_t hostPort; // this is THEIR port 74 | 75 | // this is OUR IP! 76 | uint32_t networkDestinationIp; 77 | uint16_t hostDestinationPort; 78 | 79 | uint32_t hostAck; 80 | uint32_t hostSeq; 81 | 82 | 83 | /*enum { 84 | CLOSED, 85 | SYN_ACK_SENT, 86 | SYN_SENT, 87 | ESTABLISHED 88 | } state;*/ 89 | 90 | int state; 91 | 92 | 93 | uint32_t originalHostAck; 94 | 95 | 96 | alignas(16) struct us_socket_context_t *context; 97 | 98 | int closed; 99 | int shutdown; 100 | int wants_writable; 101 | 102 | unsigned short timeout; 103 | 104 | struct us_socket_t *prev, *next; 105 | 106 | UT_hash_handle hh; 107 | }; 108 | 109 | void us_internal_socket_context_send_packet(struct us_socket_context_t *context, uint32_t hostSeq, uint32_t hostAck, uint32_t networkDestIp, uint32_t networkSourceIp, int hostDestPort, 110 | int hostSourcePort, int flagAck, int flagSyn, int flagFin, int flagRst, char *data, size_t length); 111 | 112 | #include 113 | #include 114 | #include 115 | #include 116 | #include 117 | #include 118 | #include 119 | #include 120 | #include 121 | #include 122 | #include 123 | #include 124 | #include 125 | #include 126 | #include 127 | 128 | #include 129 | #include 130 | 131 | struct us_loop_t { 132 | 133 | /* We only support one listen socket */ 134 | alignas(16) struct us_listen_socket_t *listen_socket; 135 | 136 | /* The list of closed sockets */ 137 | struct us_socket_t *close_list; 138 | 139 | // här sparar vi senaste skapade contextet 140 | struct us_socket_context_t *context; 141 | 142 | /* Post and pre callbacks */ 143 | void (*pre_cb)(struct us_loop_t *loop); 144 | void (*post_cb)(struct us_loop_t *loop); 145 | 146 | void (*wakeup_cb)(struct us_loop_t *loop); 147 | 148 | // vi har 149 | 150 | int send_fd; 151 | 152 | int fd, epfd, timer; 153 | char *buffer[1024]; 154 | size_t length[1024]; 155 | struct mmsghdr msgs[1024]; 156 | struct iovec iovecs[1024]; 157 | 158 | struct iovec messages[1024]; 159 | 160 | char *outBuffer[1024]; 161 | int queuedBuffersNum;// = 0; 162 | 163 | // timeout and then 164 | struct timespec timeout; 165 | struct timeval then; 166 | 167 | // statistics 168 | int packets_out_of_order; 169 | int healed_sockets; 170 | int duplicated_packets; 171 | unsigned long long packets_received; 172 | 173 | // linked list of all contexts 174 | struct us_socket_context_t *head; 175 | struct us_socket_context_t *iterator; 176 | }; 177 | 178 | // passing data from ip to tcp layer 179 | 180 | void us_internal_socket_context_read_tcp(struct us_socket_t *s, struct us_socket_context_t *context, IpHeader *ipHeader, struct TcpHeader *tcpHeader, int length); 181 | 182 | #endif // INTERNAL_H 183 | -------------------------------------------------------------------------------- /loop.c: -------------------------------------------------------------------------------- 1 | // loop implements the getting of IP packets and distributing of events 2 | 3 | 4 | 5 | /* uSockets is entierly opaque so we can use the real header straight up */ 6 | #include "../uWebSockets.js/uWebSockets/uSockets/src/libusockets.h" 7 | 8 | 9 | #include "internal.h" 10 | 11 | // we print statistics such as numer of out of sync, number of "healed" sockets due to drop, etc 12 | void print_statistics(struct us_loop_t *loop) { 13 | printf("Packets out of order so far: %d\n", loop->packets_out_of_order); 14 | printf("Healed sockets so far: %d\n", loop->healed_sockets); 15 | printf("Duplicated packets: %d\n\n", loop->duplicated_packets); 16 | printf("Packets received: %lld\n", loop->packets_received); 17 | } 18 | 19 | #include 20 | 21 | // should be more like read() from a file 22 | int fetchPackageBatch(struct us_loop_t *loop) { 23 | // wait for one is pointless here 24 | return recvmmsg(loop->fd, loop->msgs, 1024, /*MSG_WAITFORONE*/ 0, 0); 25 | } 26 | 27 | void releaseSend(struct us_loop_t *loop) { 28 | 29 | 30 | // this is not blocking, should block! 31 | 32 | // release aka send 33 | if (loop->queuedBuffersNum) { 34 | 35 | //printf("Sending %d packages\n", loop->queuedBuffersNum); 36 | 37 | struct mmsghdr sendVec[1024] = {}; 38 | struct sockaddr_in sin[1024] = {}; 39 | 40 | int packages = loop->queuedBuffersNum; 41 | 42 | for (int i = 0; i < loop->queuedBuffersNum; i++) { 43 | 44 | IpHeader *ipHeader = (IpHeader *) loop->outBuffer[i]; 45 | struct TcpHeader *tcpHeader = (struct TcpHeader *) IpHeader_getData(ipHeader); 46 | 47 | int length = IpHeader_getTotalLength(ipHeader);//ipHeader->getTotalLength(); 48 | 49 | sin[i].sin_family = AF_INET; 50 | sin[i].sin_port = tcpHeader->header.dest; 51 | sin[i].sin_addr.s_addr = ipHeader->daddr; 52 | 53 | loop->messages[i].iov_base = ipHeader; 54 | loop->messages[i].iov_len = length; 55 | 56 | // send out of order! 57 | // sendVec[i].msg_hdr.msg_iov = &messages[packages - i - 1]; 58 | // sendVec[i].msg_hdr.msg_iovlen = 1; 59 | 60 | // sendVec[i].msg_hdr.msg_name = &sin[packages - i - 1]; 61 | // sendVec[i].msg_hdr.msg_namelen = sizeof(sockaddr_in); 62 | 63 | sendVec[i].msg_hdr.msg_iov = &loop->messages[i]; 64 | sendVec[i].msg_hdr.msg_iovlen = 1; 65 | 66 | sendVec[i].msg_hdr.msg_name = &sin[i]; 67 | sendVec[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_in); 68 | 69 | } 70 | 71 | int sent = 0; 72 | 73 | // we just block until we're done 74 | while (sent != loop->queuedBuffersNum) { 75 | int tmp = sendmmsg(loop->send_fd, &sendVec[sent], loop->queuedBuffersNum - sent, 0); 76 | if (tmp > 0) { 77 | sent += tmp; 78 | } 79 | 80 | /*if (tmp != loop->queuedBuffersNum) { 81 | printf("COULD NOT SEND ALL PACKETS WITHOUT BLOCKING!\n"); 82 | exit(0); 83 | }*/ 84 | 85 | //break; 86 | } 87 | 88 | 89 | 90 | loop->queuedBuffersNum = 0; 91 | 92 | //std::cout << "Sent now" << std::endl; 93 | } 94 | } 95 | 96 | IpHeader *getIpPacket(struct us_loop_t *loop, int index, unsigned int *length) { 97 | IpHeader *ipHeader = (IpHeader *) loop->iovecs[index].iov_base; 98 | 99 | 100 | *length = loop->iovecs[index].iov_len; 101 | 102 | 103 | return ipHeader; 104 | 105 | } 106 | 107 | IpHeader *getIpPacketBuffer(struct us_loop_t *loop) { 108 | if (loop->queuedBuffersNum == 1024) { 109 | //std::cout << "Releasing IP buffers in getIpPacketBuffer" << std::endl; 110 | printf("SENDING OVERFLOW!\n"); 111 | exit(0); 112 | releaseSend(loop); 113 | } 114 | 115 | return (IpHeader *) loop->outBuffer[loop->queuedBuffersNum++]; 116 | } 117 | 118 | #include 119 | 120 | void us_internal_loop_link(struct us_loop_t *loop, struct us_socket_context_t *context) { 121 | /* Insert this context as the head of loop */ 122 | context->next = loop->head; 123 | context->prev = 0; 124 | if (loop->head) { 125 | loop->head->prev = context; 126 | } 127 | loop->head = context; 128 | } 129 | 130 | struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size) { 131 | struct us_loop_t *loop = (struct us_loop_t *) malloc(sizeof(struct us_loop_t) + ext_size); 132 | 133 | loop->listen_socket = 0; 134 | loop->context = 0; 135 | loop->close_list = 0; 136 | 137 | loop->pre_cb = pre_cb; 138 | loop->post_cb = post_cb; 139 | loop->wakeup_cb = wakeup_cb; 140 | 141 | loop->head = 0; 142 | loop->iterator = 0; 143 | 144 | // 145 | 146 | loop->packets_out_of_order = 0; 147 | loop->healed_sockets = 0; 148 | loop->duplicated_packets = 0; 149 | loop->packets_received = 0; 150 | 151 | loop->epfd = epoll_create1(0); 152 | 153 | loop->timer = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK); 154 | 155 | struct epoll_event event; 156 | event.events = EPOLLIN; 157 | event.data.u64 = 0; // mark the timer as 0 158 | epoll_ctl(loop->epfd, EPOLL_CTL_ADD, loop->timer, &event); 159 | 160 | /* An IPPROTO_RAW socket is send only. If you really want to receive 161 | all IP packets, use a packet(7) socket with the ETH_P_IP protocol. 162 | Note that packet sockets don't reassemble IP fragments, unlike raw 163 | sockets. */ 164 | 165 | loop->send_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 166 | 167 | loop->fd = socket(AF_INET, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_TCP); // close(fd) vid stängning 168 | if (loop->fd == -1) { 169 | //throw IP_ERR; 170 | 171 | printf("Kan inte skapa IP socket!\n"); 172 | exit(0); 173 | } 174 | 175 | //struct epoll_event event; 176 | event.events = EPOLLIN; 177 | event.data.u64 = 1; // mark the raw socket as 1 178 | epoll_ctl(loop->epfd, EPOLL_CTL_ADD, loop->fd, &event); 179 | 180 | for (int i = 0; i < 1024; i++) { 181 | loop->buffer[i] = malloc(1024 * 32); 182 | loop->outBuffer[i] = malloc(1024 * 32); 183 | } 184 | 185 | printf("Loop's first out buffer is: %p\n", loop->outBuffer[0]); 186 | 187 | const int VLEN = 1024; 188 | 189 | memset(loop->msgs, 0, sizeof(loop->msgs)); 190 | for (int i = 0; i < VLEN; i++) { 191 | loop->iovecs[i].iov_base = loop->buffer[i]; 192 | loop->iovecs[i].iov_len = 1024 * 32; 193 | loop->msgs[i].msg_hdr.msg_iov = &loop->iovecs[i]; 194 | loop->msgs[i].msg_hdr.msg_iovlen = 1; 195 | } 196 | 197 | int one = 1; 198 | const int *val = &one; 199 | if (setsockopt (loop->fd, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0) { 200 | //throw IP_ERR; 201 | } 202 | 203 | loop->queuedBuffersNum = 0; 204 | 205 | return loop; 206 | } 207 | 208 | void us_wakeup_loop(struct us_loop_t *loop) { 209 | /* We do this immediately as of now, could be delayed to next iteration */ 210 | loop->wakeup_cb(loop); 211 | } 212 | 213 | void us_loop_free(struct us_loop_t *loop) { 214 | free(loop); 215 | } 216 | 217 | void *us_loop_ext(struct us_loop_t *loop) { 218 | return loop + 1; 219 | } 220 | 221 | #include "internal.h" 222 | 223 | // we have this somewhere 224 | extern struct us_socket_t *global_s; 225 | 226 | /* Either the loop holds all sockets or the socket context do? */ 227 | #include "uthash.h" 228 | 229 | /* 230 | * 231 | * #define HASH_FIND_INT(head,findint,out) \ 232 | HASH_FIND(hh,head,findint,sizeof(int),out)*/ 233 | 234 | // us_internal_socket_context_read_tcp borde returnera själva socketen antingen utbytt, skapad eller så 235 | // sen kan vi i loopen hålla koll på alla sockets i en hashmap från 64+32 = 96 bit hash key = 12 bytes 236 | 237 | 238 | 239 | // this is the hash table 240 | struct us_socket_t *sockets = NULL; 241 | 242 | void remove_socket(struct us_socket_t *s) { 243 | 244 | HASH_DEL(sockets, s); 245 | 246 | free(s); 247 | 248 | global_s = 0; 249 | } 250 | 251 | /* We also need to merge this with uSockets and enable TLS over it */ 252 | /* WITH_USERSPACE=1 */ 253 | 254 | void us_internal_small_tick(); 255 | 256 | /* We need to have timeout by now, and every tick should print statistics on packet loss */ 257 | void us_loop_run(struct us_loop_t *loop) { 258 | printf("Getting ip packets now\n"); 259 | 260 | int repeat_ms = 250; 261 | int ms = 250; 262 | 263 | int small_ticks = 0; 264 | 265 | struct itimerspec timer_spec = { 266 | {repeat_ms / 1000, ((long)repeat_ms * 1000000) % 1000000000}, 267 | {ms / 1000, ((long)ms * 1000000) % 1000000000} 268 | }; 269 | 270 | timerfd_settime(loop->timer, 0, &timer_spec, NULL); 271 | 272 | while(1) { 273 | // epoll_wait on two fds: time and raw socket 274 | struct epoll_event events[2]; 275 | int numEvents = epoll_wait(loop->epfd, events, 2, -1); 276 | 277 | for (int i = 0; i < numEvents; i++) { 278 | if (events[i].data.u64 == 0) { 279 | //printf("Timerfd tick!\n"); 280 | 281 | us_internal_small_tick(); 282 | 283 | small_ticks++; 284 | if (small_ticks == 16) { 285 | //print_statistics(loop); 286 | us_internal_timer_sweep(loop); 287 | small_ticks = 0; 288 | } 289 | 290 | uint64_t buf; 291 | read(loop->timer, &buf, 8); 292 | } 293 | 294 | if (events[i].data.u64 == 1) { 295 | // a packet 296 | 297 | 298 | 299 | int messages = fetchPackageBatch(loop); 300 | 301 | // should never happen 302 | if (messages == -1) { 303 | continue; 304 | } 305 | 306 | //printf("Read %d packets\n", messages); 307 | 308 | // this should never happen, if it does we read too slowly and lag behind (but should be reset whatever we miss anyways) 309 | if (messages == 1024) { 310 | printf("WE ARE NOT READING PACKAGES FAST ENOUGH!\n"); 311 | //exit(0); 312 | } 313 | 314 | for (int i = 0; i < messages; i++) { 315 | unsigned int length; 316 | IpHeader *ipHeader = getIpPacket(loop, i, &length); 317 | 318 | /* First we filter out everything that isn't tcp over ipv4 */ 319 | if (ipHeader->version != 4 || ipHeader->protocol != IPPROTO_TCP) { 320 | continue; 321 | } 322 | 323 | /* Now we know this is tcp */ 324 | struct TcpHeader *tcpHeader = (struct TcpHeader *) IpHeader_getData(ipHeader); 325 | 326 | /* OMG! WE DO NOT HANDLE DUPLICATE SYN! PROPERLY!! */ 327 | 328 | // has to be: try and get this socket, in case it exists, handle the segment otherwise crete a new socket for SYN 329 | 330 | /* Is this packet SYN? */ 331 | if (tcpHeader->header.syn && !tcpHeader->header.ack) { 332 | /* Loop over all contexts */ 333 | for (struct us_socket_context_t *context = loop->context; context; context = context->next) { 334 | /* Loop over all listen sockets */ 335 | for (struct us_listen_socket_t *listen_socket = context->listen_socket; listen_socket; ) { 336 | 337 | if (listen_socket->port == TcpHeader_getDestinationPort(tcpHeader)) { 338 | //global_s = 0; 339 | 340 | /* NOTE: WE NEED TO CHECK IF OR NOT THIS SOCKET ALREADY EXISTS!!! */ 341 | struct SOCKET_KEY key = { 342 | TcpHeader_getSourcePort(tcpHeader), 343 | TcpHeader_getDestinationPort(tcpHeader), 344 | ipHeader->saddr, 345 | ipHeader->daddr 346 | }; 347 | 348 | struct us_socket_t *s; 349 | HASH_FIND(hh, sockets, &key, sizeof(struct SOCKET_KEY), s); 350 | if (s) { 351 | printf("GOT DUPLICATE SYN!!!!!!\n"); 352 | goto here; 353 | } 354 | 355 | us_internal_socket_context_read_tcp(NULL, listen_socket->context, ipHeader, tcpHeader, length); 356 | 357 | // vi kommer att ha ändrat global_s om det finns en ny socket! 358 | if (global_s) { 359 | struct SOCKET_KEY key = { 360 | TcpHeader_getSourcePort(tcpHeader), 361 | TcpHeader_getDestinationPort(tcpHeader), 362 | ipHeader->saddr, 363 | ipHeader->daddr 364 | }; 365 | 366 | global_s->key = key; 367 | 368 | HASH_ADD(hh, sockets, key, sizeof(struct SOCKET_KEY), global_s); 369 | 370 | global_s = 0; 371 | } 372 | 373 | } 374 | 375 | /* We only have one listen socket for now */ 376 | break; 377 | } 378 | } 379 | } else { 380 | 381 | struct us_socket_t *s; 382 | struct SOCKET_KEY key = { 383 | TcpHeader_getSourcePort(tcpHeader), 384 | TcpHeader_getDestinationPort(tcpHeader), 385 | ipHeader->saddr, 386 | ipHeader->daddr 387 | }; 388 | 389 | HASH_FIND(hh, sockets, &key, sizeof(struct SOCKET_KEY), s); 390 | 391 | if (s) { 392 | us_internal_socket_context_read_tcp(s, s->context, ipHeader, tcpHeader, length); 393 | } 394 | 395 | } 396 | 397 | here: 398 | continue; 399 | } 400 | 401 | releaseSend(loop); 402 | } 403 | } 404 | 405 | } 406 | } 407 | 408 | 409 | // vi behöver få in timers, och svepa över de websockets som inte får något meddelande i tid - som en absolut måttstock på stabilitet oavsett outof order etc 410 | 411 | void us_internal_timer_sweep(struct us_loop_t *loop) { 412 | for (loop->iterator = loop->head; loop->iterator; loop->iterator = loop->iterator->next) { 413 | 414 | struct us_socket_context_t *context = loop->iterator; 415 | for (context->iterator = context->head; context->iterator; ) { 416 | 417 | struct us_socket_t *s = context->iterator; 418 | if (s->timeout && --(s->timeout) == 0) { 419 | 420 | context->on_socket_timeout(s); 421 | 422 | /* Check for unlink / link */ 423 | if (s == context->iterator) { 424 | context->iterator = s->next; 425 | } 426 | } else { 427 | context->iterator = s->next; 428 | } 429 | } 430 | } 431 | } -------------------------------------------------------------------------------- /server.c: -------------------------------------------------------------------------------- 1 | /* uSockets is entierly opaque so we can use the real header straight up */ 2 | #include "../uWebSockets.js/uWebSockets/uSockets/src/libusockets.h" 3 | 4 | int connections = 0; 5 | int disconnections = 0; 6 | 7 | #include 8 | 9 | struct us_socket_t *on_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) { 10 | connections++; 11 | 12 | if (connections % 100 == 0) { 13 | printf("Connections: %d, disconnections: %d\n", connections, disconnections); 14 | } 15 | 16 | /* We need a ping every now and then */ 17 | us_socket_timeout(0, s, 32); 18 | 19 | return s; 20 | } 21 | 22 | struct us_socket_t *on_close(struct us_socket_t *s) { 23 | disconnections++; 24 | 25 | if (connections % 100 == 0) { 26 | printf("Connections: %d, disconnections: %d\n", connections, disconnections); 27 | } 28 | 29 | return s; 30 | } 31 | 32 | struct us_socket_t *on_data(struct us_socket_t *s, char *data, int length) { 33 | /* Gettings data (ping) resets the timeout */ 34 | us_socket_timeout(0, s, 32); 35 | 36 | /* For now we try and send data also */ 37 | //us_socket_write() 38 | 39 | return s; 40 | } 41 | 42 | struct us_socket_t *on_timeout(struct us_socket_t *s) { 43 | 44 | us_socket_close(0, s); 45 | 46 | return s; 47 | } 48 | 49 | int main() { 50 | struct us_loop_t *loop = us_create_loop(NULL, NULL, NULL, NULL, 0); 51 | 52 | struct us_socket_context_options_t options = {}; 53 | struct us_socket_context_t *context = us_create_socket_context(0, loop, 0, options); 54 | 55 | us_socket_context_on_open(0, context, on_open); 56 | us_socket_context_on_close(0, context, on_close); 57 | us_socket_context_on_data(0, context, on_data); 58 | us_socket_context_on_timeout(0, context, on_timeout); 59 | 60 | us_socket_context_listen(0, context, NULL, 4000, 0, 0); 61 | 62 | us_loop_run(loop); 63 | } 64 | -------------------------------------------------------------------------------- /socket.c: -------------------------------------------------------------------------------- 1 | /* uSockets is entierly opaque so we can use the real header straight up */ 2 | #include "../uWebSockets.js/uWebSockets/uSockets/src/libusockets.h" 3 | 4 | #include "internal.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) { 12 | 13 | if (!length) { 14 | return 0; 15 | } 16 | 17 | us_internal_socket_context_send_packet(s->context, s->hostSeq, s->hostAck, s->networkIp, s->networkDestinationIp, s->hostPort, s->hostDestinationPort, 1, 0, 0, 0, data, length); 18 | s->hostSeq += length; 19 | 20 | /* Send everything */ 21 | return length; 22 | } 23 | 24 | void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds) { 25 | if (seconds) { 26 | unsigned short timeout_sweeps = (unsigned short) (0.5f + ((float) seconds) / ((float) LIBUS_TIMEOUT_GRANULARITY)); 27 | s->timeout = timeout_sweeps ? timeout_sweeps : 1; 28 | } else { 29 | s->timeout = 0; 30 | } 31 | } 32 | 33 | void *us_socket_ext(int ssl, struct us_socket_t *s) { 34 | //printf("socket ext: %p\n", s + 1); 35 | return s + 1; 36 | } 37 | 38 | struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s) { 39 | //printf("Socket context: %p\n", s->context); 40 | return s->context; 41 | } 42 | 43 | void us_socket_flush(int ssl, struct us_socket_t *s) { 44 | 45 | } 46 | 47 | void us_socket_shutdown(int ssl, struct us_socket_t *s) { 48 | s->shutdown = 1; 49 | } 50 | 51 | int us_socket_is_shut_down(int ssl, struct us_socket_t *s) { 52 | return s->shutdown; 53 | } 54 | 55 | int us_socket_is_closed(int ssl, struct us_socket_t *s) { 56 | return s->closed; 57 | } 58 | 59 | struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s) { 60 | 61 | if (!us_socket_is_closed(0, s)) { 62 | /* Emit close event */ 63 | s = s->context->on_close(s); 64 | } 65 | 66 | /* We are now closed */ 67 | s->closed = 1; 68 | 69 | /* Add us to the close list */ 70 | 71 | printf("closing socket is not implemented!\n"); 72 | 73 | return s; 74 | } 75 | 76 | void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *length) { 77 | *length = 0; 78 | } 79 | -------------------------------------------------------------------------------- /stress.c: -------------------------------------------------------------------------------- 1 | /* uSockets is entierly opaque so we can use the real header straight up */ 2 | #include "../uWebSockets.js/uWebSockets/uSockets/src/libusockets.h" 3 | 4 | int connections = 0; 5 | 6 | #include 7 | 8 | struct us_socket_t *on_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) { 9 | printf("TCP socket accepted\n"); 10 | 11 | return s; 12 | } 13 | 14 | struct us_socket_t *on_close(struct us_socket_t *s) { 15 | printf("TCP socket closed\n"); 16 | 17 | return s; 18 | } 19 | 20 | struct us_socket_t *on_data(struct us_socket_t *s, char *data, int length) { 21 | printf("TCP socket data with length: %d\n", length); 22 | 23 | // echo it back 24 | us_socket_write(0, s, data, length, 0); 25 | 26 | return s; 27 | } 28 | 29 | int main() { 30 | struct us_loop_t *loop = us_create_loop(NULL, NULL, NULL, NULL, 0); 31 | 32 | struct us_socket_context_options_t options = {}; 33 | struct us_socket_context_t *context = us_create_socket_context(0, loop, 0, options); 34 | 35 | us_socket_context_on_open(0, context, on_open); 36 | us_socket_context_on_close(0, context, on_close); 37 | us_socket_context_on_data(0, context, on_data); 38 | 39 | us_socket_context_listen(0, context, "127.0.0.1", 4000, 0, 0); 40 | 41 | us_socket_context_connect(0, context, "127.0.0.1", 4000, NULL, 0, 0); 42 | 43 | us_loop_run(loop); 44 | } 45 | -------------------------------------------------------------------------------- /tcp.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console c++11 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | SOURCES += \ 7 | context.c \ 8 | loop.c \ 9 | main.c \ 10 | socket.c 11 | 12 | HEADERS += \ 13 | Packets.h \ 14 | internal.h \ 15 | uthash.h 16 | 17 | QMAKE_CXXFLAGS += -fsanitize=address 18 | LIBS += -lasan 19 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /* uSockets is entierly opaque so we can use the real header straight up */ 2 | #include "../uWebSockets.js/uWebSockets/uSockets/src/libusockets.h" 3 | 4 | int connections = 0; 5 | 6 | #include 7 | 8 | struct us_socket_t *on_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) { 9 | printf("TCP socket accepted\n"); 10 | 11 | return s; 12 | } 13 | 14 | struct us_socket_t *on_close(struct us_socket_t *s) { 15 | printf("TCP socket closed\n"); 16 | 17 | return s; 18 | } 19 | 20 | struct us_socket_t *on_data(struct us_socket_t *s, char *data, int length) { 21 | printf("TCP socket data with length: %d\n", length); 22 | 23 | // echo it back 24 | us_socket_write(0, s, data, length, 0); 25 | 26 | return s; 27 | } 28 | 29 | int main() { 30 | struct us_loop_t *loop = us_create_loop(NULL, NULL, NULL, NULL, 0); 31 | 32 | struct us_socket_context_options_t options = {}; 33 | struct us_socket_context_t *context = us_create_socket_context(0, loop, 0, options); 34 | 35 | us_socket_context_on_open(0, context, on_open); 36 | us_socket_context_on_close(0, context, on_close); 37 | us_socket_context_on_data(0, context, on_data); 38 | 39 | us_socket_context_listen(0, context, "127.0.0.1", 4000, 0, 0); 40 | us_loop_run(loop); 41 | } 42 | -------------------------------------------------------------------------------- /uthash.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifndef UTHASH_H 25 | #define UTHASH_H 26 | 27 | #define UTHASH_VERSION 2.1.0 28 | 29 | #include /* memcmp, memset, strlen */ 30 | #include /* ptrdiff_t */ 31 | #include /* exit */ 32 | 33 | /* These macros use decltype or the earlier __typeof GNU extension. 34 | As decltype is only available in newer compilers (VS2010 or gcc 4.3+ 35 | when compiling c++ source) this code uses whatever method is needed 36 | or, for VS2008 where neither is available, uses casting workarounds. */ 37 | #if !defined(DECLTYPE) && !defined(NO_DECLTYPE) 38 | #if defined(_MSC_VER) /* MS compiler */ 39 | #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ 40 | #define DECLTYPE(x) (decltype(x)) 41 | #else /* VS2008 or older (or VS2010 in C mode) */ 42 | #define NO_DECLTYPE 43 | #endif 44 | #elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) 45 | #define NO_DECLTYPE 46 | #else /* GNU, Sun and other compilers */ 47 | #define DECLTYPE(x) (__typeof(x)) 48 | #endif 49 | #endif 50 | 51 | #ifdef NO_DECLTYPE 52 | #define DECLTYPE(x) 53 | #define DECLTYPE_ASSIGN(dst,src) \ 54 | do { \ 55 | char **_da_dst = (char**)(&(dst)); \ 56 | *_da_dst = (char*)(src); \ 57 | } while (0) 58 | #else 59 | #define DECLTYPE_ASSIGN(dst,src) \ 60 | do { \ 61 | (dst) = DECLTYPE(dst)(src); \ 62 | } while (0) 63 | #endif 64 | 65 | /* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ 66 | #if defined(_WIN32) 67 | #if defined(_MSC_VER) && _MSC_VER >= 1600 68 | #include 69 | #elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) 70 | #include 71 | #else 72 | typedef unsigned int uint32_t; 73 | typedef unsigned char uint8_t; 74 | #endif 75 | #elif defined(__GNUC__) && !defined(__VXWORKS__) 76 | #include 77 | #else 78 | typedef unsigned int uint32_t; 79 | typedef unsigned char uint8_t; 80 | #endif 81 | 82 | #ifndef uthash_malloc 83 | #define uthash_malloc(sz) malloc(sz) /* malloc fcn */ 84 | #endif 85 | #ifndef uthash_free 86 | #define uthash_free(ptr,sz) free(ptr) /* free fcn */ 87 | #endif 88 | #ifndef uthash_bzero 89 | #define uthash_bzero(a,n) memset(a,'\0',n) 90 | #endif 91 | #ifndef uthash_strlen 92 | #define uthash_strlen(s) strlen(s) 93 | #endif 94 | 95 | #ifdef uthash_memcmp 96 | /* This warning will not catch programs that define uthash_memcmp AFTER including uthash.h. */ 97 | #warning "uthash_memcmp is deprecated; please use HASH_KEYCMP instead" 98 | #else 99 | #define uthash_memcmp(a,b,n) memcmp(a,b,n) 100 | #endif 101 | 102 | #ifndef HASH_KEYCMP 103 | #define HASH_KEYCMP(a,b,n) uthash_memcmp(a,b,n) 104 | #endif 105 | 106 | #ifndef uthash_noexpand_fyi 107 | #define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ 108 | #endif 109 | #ifndef uthash_expand_fyi 110 | #define uthash_expand_fyi(tbl) /* can be defined to log expands */ 111 | #endif 112 | 113 | #ifndef HASH_NONFATAL_OOM 114 | #define HASH_NONFATAL_OOM 0 115 | #endif 116 | 117 | #if HASH_NONFATAL_OOM 118 | /* malloc failures can be recovered from */ 119 | 120 | #ifndef uthash_nonfatal_oom 121 | #define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ 122 | #endif 123 | 124 | #define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) 125 | #define IF_HASH_NONFATAL_OOM(x) x 126 | 127 | #else 128 | /* malloc failures result in lost memory, hash tables are unusable */ 129 | 130 | #ifndef uthash_fatal 131 | #define uthash_fatal(msg) exit(-1) /* fatal OOM error */ 132 | #endif 133 | 134 | #define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") 135 | #define IF_HASH_NONFATAL_OOM(x) 136 | 137 | #endif 138 | 139 | /* initial number of buckets */ 140 | #define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ 141 | #define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ 142 | #define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ 143 | 144 | /* calculate the element whose hash handle address is hhp */ 145 | #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) 146 | /* calculate the hash handle from element address elp */ 147 | #define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) 148 | 149 | #define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ 150 | do { \ 151 | struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ 152 | unsigned _hd_bkt; \ 153 | HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ 154 | (head)->hh.tbl->buckets[_hd_bkt].count++; \ 155 | _hd_hh_item->hh_next = NULL; \ 156 | _hd_hh_item->hh_prev = NULL; \ 157 | } while (0) 158 | 159 | #define HASH_VALUE(keyptr,keylen,hashv) \ 160 | do { \ 161 | HASH_FCN(keyptr, keylen, hashv); \ 162 | } while (0) 163 | 164 | #define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ 165 | do { \ 166 | (out) = NULL; \ 167 | if (head) { \ 168 | unsigned _hf_bkt; \ 169 | HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ 170 | if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ 171 | HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ 172 | } \ 173 | } \ 174 | } while (0) 175 | 176 | #define HASH_FIND(hh,head,keyptr,keylen,out) \ 177 | do { \ 178 | (out) = NULL; \ 179 | if (head) { \ 180 | unsigned _hf_hashv; \ 181 | HASH_VALUE(keyptr, keylen, _hf_hashv); \ 182 | HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ 183 | } \ 184 | } while (0) 185 | 186 | #ifdef HASH_BLOOM 187 | #define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) 188 | #define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) 189 | #define HASH_BLOOM_MAKE(tbl,oomed) \ 190 | do { \ 191 | (tbl)->bloom_nbits = HASH_BLOOM; \ 192 | (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ 193 | if (!(tbl)->bloom_bv) { \ 194 | HASH_RECORD_OOM(oomed); \ 195 | } else { \ 196 | uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ 197 | (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ 198 | } \ 199 | } while (0) 200 | 201 | #define HASH_BLOOM_FREE(tbl) \ 202 | do { \ 203 | uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ 204 | } while (0) 205 | 206 | #define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) 207 | #define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) 208 | 209 | #define HASH_BLOOM_ADD(tbl,hashv) \ 210 | HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) 211 | 212 | #define HASH_BLOOM_TEST(tbl,hashv) \ 213 | HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) 214 | 215 | #else 216 | #define HASH_BLOOM_MAKE(tbl,oomed) 217 | #define HASH_BLOOM_FREE(tbl) 218 | #define HASH_BLOOM_ADD(tbl,hashv) 219 | #define HASH_BLOOM_TEST(tbl,hashv) (1) 220 | #define HASH_BLOOM_BYTELEN 0U 221 | #endif 222 | 223 | #define HASH_MAKE_TABLE(hh,head,oomed) \ 224 | do { \ 225 | (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ 226 | if (!(head)->hh.tbl) { \ 227 | HASH_RECORD_OOM(oomed); \ 228 | } else { \ 229 | uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ 230 | (head)->hh.tbl->tail = &((head)->hh); \ 231 | (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ 232 | (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ 233 | (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ 234 | (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ 235 | HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ 236 | (head)->hh.tbl->signature = HASH_SIGNATURE; \ 237 | if (!(head)->hh.tbl->buckets) { \ 238 | HASH_RECORD_OOM(oomed); \ 239 | uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ 240 | } else { \ 241 | uthash_bzero((head)->hh.tbl->buckets, \ 242 | HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ 243 | HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ 244 | IF_HASH_NONFATAL_OOM( \ 245 | if (oomed) { \ 246 | uthash_free((head)->hh.tbl->buckets, \ 247 | HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ 248 | uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ 249 | } \ 250 | ) \ 251 | } \ 252 | } \ 253 | } while (0) 254 | 255 | #define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ 256 | do { \ 257 | (replaced) = NULL; \ 258 | HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ 259 | if (replaced) { \ 260 | HASH_DELETE(hh, head, replaced); \ 261 | } \ 262 | HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ 263 | } while (0) 264 | 265 | #define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ 266 | do { \ 267 | (replaced) = NULL; \ 268 | HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ 269 | if (replaced) { \ 270 | HASH_DELETE(hh, head, replaced); \ 271 | } \ 272 | HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ 273 | } while (0) 274 | 275 | #define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ 276 | do { \ 277 | unsigned _hr_hashv; \ 278 | HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ 279 | HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ 280 | } while (0) 281 | 282 | #define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ 283 | do { \ 284 | unsigned _hr_hashv; \ 285 | HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ 286 | HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ 287 | } while (0) 288 | 289 | #define HASH_APPEND_LIST(hh, head, add) \ 290 | do { \ 291 | (add)->hh.next = NULL; \ 292 | (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ 293 | (head)->hh.tbl->tail->next = (add); \ 294 | (head)->hh.tbl->tail = &((add)->hh); \ 295 | } while (0) 296 | 297 | #define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ 298 | do { \ 299 | do { \ 300 | if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ 301 | break; \ 302 | } \ 303 | } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ 304 | } while (0) 305 | 306 | #ifdef NO_DECLTYPE 307 | #undef HASH_AKBI_INNER_LOOP 308 | #define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ 309 | do { \ 310 | char *_hs_saved_head = (char*)(head); \ 311 | do { \ 312 | DECLTYPE_ASSIGN(head, _hs_iter); \ 313 | if (cmpfcn(head, add) > 0) { \ 314 | DECLTYPE_ASSIGN(head, _hs_saved_head); \ 315 | break; \ 316 | } \ 317 | DECLTYPE_ASSIGN(head, _hs_saved_head); \ 318 | } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ 319 | } while (0) 320 | #endif 321 | 322 | #if HASH_NONFATAL_OOM 323 | 324 | #define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ 325 | do { \ 326 | if (!(oomed)) { \ 327 | unsigned _ha_bkt; \ 328 | (head)->hh.tbl->num_items++; \ 329 | HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ 330 | HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ 331 | if (oomed) { \ 332 | HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ 333 | HASH_DELETE_HH(hh, head, &(add)->hh); \ 334 | (add)->hh.tbl = NULL; \ 335 | uthash_nonfatal_oom(add); \ 336 | } else { \ 337 | HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ 338 | HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ 339 | } \ 340 | } else { \ 341 | (add)->hh.tbl = NULL; \ 342 | uthash_nonfatal_oom(add); \ 343 | } \ 344 | } while (0) 345 | 346 | #else 347 | 348 | #define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ 349 | do { \ 350 | unsigned _ha_bkt; \ 351 | (head)->hh.tbl->num_items++; \ 352 | HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ 353 | HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ 354 | HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ 355 | HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ 356 | } while (0) 357 | 358 | #endif 359 | 360 | 361 | #define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ 362 | do { \ 363 | IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ 364 | (add)->hh.hashv = (hashval); \ 365 | (add)->hh.key = (char*) (keyptr); \ 366 | (add)->hh.keylen = (unsigned) (keylen_in); \ 367 | if (!(head)) { \ 368 | (add)->hh.next = NULL; \ 369 | (add)->hh.prev = NULL; \ 370 | HASH_MAKE_TABLE(hh, add, _ha_oomed); \ 371 | IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ 372 | (head) = (add); \ 373 | IF_HASH_NONFATAL_OOM( } ) \ 374 | } else { \ 375 | void *_hs_iter = (head); \ 376 | (add)->hh.tbl = (head)->hh.tbl; \ 377 | HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ 378 | if (_hs_iter) { \ 379 | (add)->hh.next = _hs_iter; \ 380 | if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ 381 | HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ 382 | } else { \ 383 | (head) = (add); \ 384 | } \ 385 | HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ 386 | } else { \ 387 | HASH_APPEND_LIST(hh, head, add); \ 388 | } \ 389 | } \ 390 | HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ 391 | HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ 392 | } while (0) 393 | 394 | #define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ 395 | do { \ 396 | unsigned _hs_hashv; \ 397 | HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ 398 | HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ 399 | } while (0) 400 | 401 | #define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ 402 | HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) 403 | 404 | #define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ 405 | HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) 406 | 407 | #define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ 408 | do { \ 409 | IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ 410 | (add)->hh.hashv = (hashval); \ 411 | (add)->hh.key = (char*) (keyptr); \ 412 | (add)->hh.keylen = (unsigned) (keylen_in); \ 413 | if (!(head)) { \ 414 | (add)->hh.next = NULL; \ 415 | (add)->hh.prev = NULL; \ 416 | HASH_MAKE_TABLE(hh, add, _ha_oomed); \ 417 | IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ 418 | (head) = (add); \ 419 | IF_HASH_NONFATAL_OOM( } ) \ 420 | } else { \ 421 | (add)->hh.tbl = (head)->hh.tbl; \ 422 | HASH_APPEND_LIST(hh, head, add); \ 423 | } \ 424 | HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ 425 | HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ 426 | } while (0) 427 | 428 | #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ 429 | do { \ 430 | unsigned _ha_hashv; \ 431 | HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ 432 | HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ 433 | } while (0) 434 | 435 | #define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ 436 | HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) 437 | 438 | #define HASH_ADD(hh,head,fieldname,keylen_in,add) \ 439 | HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) 440 | 441 | #define HASH_TO_BKT(hashv,num_bkts,bkt) \ 442 | do { \ 443 | bkt = ((hashv) & ((num_bkts) - 1U)); \ 444 | } while (0) 445 | 446 | /* delete "delptr" from the hash table. 447 | * "the usual" patch-up process for the app-order doubly-linked-list. 448 | * The use of _hd_hh_del below deserves special explanation. 449 | * These used to be expressed using (delptr) but that led to a bug 450 | * if someone used the same symbol for the head and deletee, like 451 | * HASH_DELETE(hh,users,users); 452 | * We want that to work, but by changing the head (users) below 453 | * we were forfeiting our ability to further refer to the deletee (users) 454 | * in the patch-up process. Solution: use scratch space to 455 | * copy the deletee pointer, then the latter references are via that 456 | * scratch pointer rather than through the repointed (users) symbol. 457 | */ 458 | #define HASH_DELETE(hh,head,delptr) \ 459 | HASH_DELETE_HH(hh, head, &(delptr)->hh) 460 | 461 | #define HASH_DELETE_HH(hh,head,delptrhh) \ 462 | do { \ 463 | struct UT_hash_handle *_hd_hh_del = (delptrhh); \ 464 | if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ 465 | HASH_BLOOM_FREE((head)->hh.tbl); \ 466 | uthash_free((head)->hh.tbl->buckets, \ 467 | (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ 468 | uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ 469 | (head) = NULL; \ 470 | } else { \ 471 | unsigned _hd_bkt; \ 472 | if (_hd_hh_del == (head)->hh.tbl->tail) { \ 473 | (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ 474 | } \ 475 | if (_hd_hh_del->prev != NULL) { \ 476 | HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ 477 | } else { \ 478 | DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ 479 | } \ 480 | if (_hd_hh_del->next != NULL) { \ 481 | HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ 482 | } \ 483 | HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ 484 | HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ 485 | (head)->hh.tbl->num_items--; \ 486 | } \ 487 | HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ 488 | } while (0) 489 | 490 | /* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ 491 | #define HASH_FIND_STR(head,findstr,out) \ 492 | do { \ 493 | unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ 494 | HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ 495 | } while (0) 496 | #define HASH_ADD_STR(head,strfield,add) \ 497 | do { \ 498 | unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ 499 | HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ 500 | } while (0) 501 | #define HASH_REPLACE_STR(head,strfield,add,replaced) \ 502 | do { \ 503 | unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ 504 | HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ 505 | } while (0) 506 | #define HASH_FIND_INT(head,findint,out) \ 507 | HASH_FIND(hh,head,findint,sizeof(int),out) 508 | #define HASH_ADD_INT(head,intfield,add) \ 509 | HASH_ADD(hh,head,intfield,sizeof(int),add) 510 | #define HASH_REPLACE_INT(head,intfield,add,replaced) \ 511 | HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) 512 | #define HASH_FIND_PTR(head,findptr,out) \ 513 | HASH_FIND(hh,head,findptr,sizeof(void *),out) 514 | #define HASH_ADD_PTR(head,ptrfield,add) \ 515 | HASH_ADD(hh,head,ptrfield,sizeof(void *),add) 516 | #define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ 517 | HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) 518 | #define HASH_DEL(head,delptr) \ 519 | HASH_DELETE(hh,head,delptr) 520 | 521 | /* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. 522 | * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. 523 | */ 524 | #ifdef HASH_DEBUG 525 | #include /* fprintf, stderr */ 526 | #define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) 527 | #define HASH_FSCK(hh,head,where) \ 528 | do { \ 529 | struct UT_hash_handle *_thh; \ 530 | if (head) { \ 531 | unsigned _bkt_i; \ 532 | unsigned _count = 0; \ 533 | char *_prev; \ 534 | for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ 535 | unsigned _bkt_count = 0; \ 536 | _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ 537 | _prev = NULL; \ 538 | while (_thh) { \ 539 | if (_prev != (char*)(_thh->hh_prev)) { \ 540 | HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ 541 | (where), (void*)_thh->hh_prev, (void*)_prev); \ 542 | } \ 543 | _bkt_count++; \ 544 | _prev = (char*)(_thh); \ 545 | _thh = _thh->hh_next; \ 546 | } \ 547 | _count += _bkt_count; \ 548 | if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ 549 | HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ 550 | (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ 551 | } \ 552 | } \ 553 | if (_count != (head)->hh.tbl->num_items) { \ 554 | HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ 555 | (where), (head)->hh.tbl->num_items, _count); \ 556 | } \ 557 | _count = 0; \ 558 | _prev = NULL; \ 559 | _thh = &(head)->hh; \ 560 | while (_thh) { \ 561 | _count++; \ 562 | if (_prev != (char*)_thh->prev) { \ 563 | HASH_OOPS("%s: invalid prev %p, actual %p\n", \ 564 | (where), (void*)_thh->prev, (void*)_prev); \ 565 | } \ 566 | _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ 567 | _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ 568 | } \ 569 | if (_count != (head)->hh.tbl->num_items) { \ 570 | HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ 571 | (where), (head)->hh.tbl->num_items, _count); \ 572 | } \ 573 | } \ 574 | } while (0) 575 | #else 576 | #define HASH_FSCK(hh,head,where) 577 | #endif 578 | 579 | /* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to 580 | * the descriptor to which this macro is defined for tuning the hash function. 581 | * The app can #include to get the prototype for write(2). */ 582 | #ifdef HASH_EMIT_KEYS 583 | #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ 584 | do { \ 585 | unsigned _klen = fieldlen; \ 586 | write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ 587 | write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ 588 | } while (0) 589 | #else 590 | #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) 591 | #endif 592 | 593 | /* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ 594 | #ifdef HASH_FUNCTION 595 | #define HASH_FCN HASH_FUNCTION 596 | #else 597 | #define HASH_FCN HASH_JEN 598 | #endif 599 | 600 | /* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ 601 | #define HASH_BER(key,keylen,hashv) \ 602 | do { \ 603 | unsigned _hb_keylen = (unsigned)keylen; \ 604 | const unsigned char *_hb_key = (const unsigned char*)(key); \ 605 | (hashv) = 0; \ 606 | while (_hb_keylen-- != 0U) { \ 607 | (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ 608 | } \ 609 | } while (0) 610 | 611 | 612 | /* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at 613 | * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ 614 | #define HASH_SAX(key,keylen,hashv) \ 615 | do { \ 616 | unsigned _sx_i; \ 617 | const unsigned char *_hs_key = (const unsigned char*)(key); \ 618 | hashv = 0; \ 619 | for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ 620 | hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ 621 | } \ 622 | } while (0) 623 | /* FNV-1a variation */ 624 | #define HASH_FNV(key,keylen,hashv) \ 625 | do { \ 626 | unsigned _fn_i; \ 627 | const unsigned char *_hf_key = (const unsigned char*)(key); \ 628 | (hashv) = 2166136261U; \ 629 | for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ 630 | hashv = hashv ^ _hf_key[_fn_i]; \ 631 | hashv = hashv * 16777619U; \ 632 | } \ 633 | } while (0) 634 | 635 | #define HASH_OAT(key,keylen,hashv) \ 636 | do { \ 637 | unsigned _ho_i; \ 638 | const unsigned char *_ho_key=(const unsigned char*)(key); \ 639 | hashv = 0; \ 640 | for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ 641 | hashv += _ho_key[_ho_i]; \ 642 | hashv += (hashv << 10); \ 643 | hashv ^= (hashv >> 6); \ 644 | } \ 645 | hashv += (hashv << 3); \ 646 | hashv ^= (hashv >> 11); \ 647 | hashv += (hashv << 15); \ 648 | } while (0) 649 | 650 | #define HASH_JEN_MIX(a,b,c) \ 651 | do { \ 652 | a -= b; a -= c; a ^= ( c >> 13 ); \ 653 | b -= c; b -= a; b ^= ( a << 8 ); \ 654 | c -= a; c -= b; c ^= ( b >> 13 ); \ 655 | a -= b; a -= c; a ^= ( c >> 12 ); \ 656 | b -= c; b -= a; b ^= ( a << 16 ); \ 657 | c -= a; c -= b; c ^= ( b >> 5 ); \ 658 | a -= b; a -= c; a ^= ( c >> 3 ); \ 659 | b -= c; b -= a; b ^= ( a << 10 ); \ 660 | c -= a; c -= b; c ^= ( b >> 15 ); \ 661 | } while (0) 662 | 663 | #define HASH_JEN(key,keylen,hashv) \ 664 | do { \ 665 | unsigned _hj_i,_hj_j,_hj_k; \ 666 | unsigned const char *_hj_key=(unsigned const char*)(key); \ 667 | hashv = 0xfeedbeefu; \ 668 | _hj_i = _hj_j = 0x9e3779b9u; \ 669 | _hj_k = (unsigned)(keylen); \ 670 | while (_hj_k >= 12U) { \ 671 | _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ 672 | + ( (unsigned)_hj_key[2] << 16 ) \ 673 | + ( (unsigned)_hj_key[3] << 24 ) ); \ 674 | _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ 675 | + ( (unsigned)_hj_key[6] << 16 ) \ 676 | + ( (unsigned)_hj_key[7] << 24 ) ); \ 677 | hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ 678 | + ( (unsigned)_hj_key[10] << 16 ) \ 679 | + ( (unsigned)_hj_key[11] << 24 ) ); \ 680 | \ 681 | HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ 682 | \ 683 | _hj_key += 12; \ 684 | _hj_k -= 12U; \ 685 | } \ 686 | hashv += (unsigned)(keylen); \ 687 | switch ( _hj_k ) { \ 688 | case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ 689 | case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ 690 | case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ 691 | case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ 692 | case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ 693 | case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ 694 | case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ 695 | case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ 696 | case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ 697 | case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ 698 | case 1: _hj_i += _hj_key[0]; \ 699 | } \ 700 | HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ 701 | } while (0) 702 | 703 | /* The Paul Hsieh hash function */ 704 | #undef get16bits 705 | #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ 706 | || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) 707 | #define get16bits(d) (*((const uint16_t *) (d))) 708 | #endif 709 | 710 | #if !defined (get16bits) 711 | #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ 712 | +(uint32_t)(((const uint8_t *)(d))[0]) ) 713 | #endif 714 | #define HASH_SFH(key,keylen,hashv) \ 715 | do { \ 716 | unsigned const char *_sfh_key=(unsigned const char*)(key); \ 717 | uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ 718 | \ 719 | unsigned _sfh_rem = _sfh_len & 3U; \ 720 | _sfh_len >>= 2; \ 721 | hashv = 0xcafebabeu; \ 722 | \ 723 | /* Main loop */ \ 724 | for (;_sfh_len > 0U; _sfh_len--) { \ 725 | hashv += get16bits (_sfh_key); \ 726 | _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ 727 | hashv = (hashv << 16) ^ _sfh_tmp; \ 728 | _sfh_key += 2U*sizeof (uint16_t); \ 729 | hashv += hashv >> 11; \ 730 | } \ 731 | \ 732 | /* Handle end cases */ \ 733 | switch (_sfh_rem) { \ 734 | case 3: hashv += get16bits (_sfh_key); \ 735 | hashv ^= hashv << 16; \ 736 | hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ 737 | hashv += hashv >> 11; \ 738 | break; \ 739 | case 2: hashv += get16bits (_sfh_key); \ 740 | hashv ^= hashv << 11; \ 741 | hashv += hashv >> 17; \ 742 | break; \ 743 | case 1: hashv += *_sfh_key; \ 744 | hashv ^= hashv << 10; \ 745 | hashv += hashv >> 1; \ 746 | } \ 747 | \ 748 | /* Force "avalanching" of final 127 bits */ \ 749 | hashv ^= hashv << 3; \ 750 | hashv += hashv >> 5; \ 751 | hashv ^= hashv << 4; \ 752 | hashv += hashv >> 17; \ 753 | hashv ^= hashv << 25; \ 754 | hashv += hashv >> 6; \ 755 | } while (0) 756 | 757 | /* iterate over items in a known bucket to find desired item */ 758 | #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ 759 | do { \ 760 | if ((head).hh_head != NULL) { \ 761 | DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ 762 | } else { \ 763 | (out) = NULL; \ 764 | } \ 765 | while ((out) != NULL) { \ 766 | if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ 767 | if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ 768 | break; \ 769 | } \ 770 | } \ 771 | if ((out)->hh.hh_next != NULL) { \ 772 | DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ 773 | } else { \ 774 | (out) = NULL; \ 775 | } \ 776 | } \ 777 | } while (0) 778 | 779 | /* add an item to a bucket */ 780 | #define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ 781 | do { \ 782 | UT_hash_bucket *_ha_head = &(head); \ 783 | _ha_head->count++; \ 784 | (addhh)->hh_next = _ha_head->hh_head; \ 785 | (addhh)->hh_prev = NULL; \ 786 | if (_ha_head->hh_head != NULL) { \ 787 | _ha_head->hh_head->hh_prev = (addhh); \ 788 | } \ 789 | _ha_head->hh_head = (addhh); \ 790 | if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ 791 | && !(addhh)->tbl->noexpand) { \ 792 | HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ 793 | IF_HASH_NONFATAL_OOM( \ 794 | if (oomed) { \ 795 | HASH_DEL_IN_BKT(head,addhh); \ 796 | } \ 797 | ) \ 798 | } \ 799 | } while (0) 800 | 801 | /* remove an item from a given bucket */ 802 | #define HASH_DEL_IN_BKT(head,delhh) \ 803 | do { \ 804 | UT_hash_bucket *_hd_head = &(head); \ 805 | _hd_head->count--; \ 806 | if (_hd_head->hh_head == (delhh)) { \ 807 | _hd_head->hh_head = (delhh)->hh_next; \ 808 | } \ 809 | if ((delhh)->hh_prev) { \ 810 | (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ 811 | } \ 812 | if ((delhh)->hh_next) { \ 813 | (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ 814 | } \ 815 | } while (0) 816 | 817 | /* Bucket expansion has the effect of doubling the number of buckets 818 | * and redistributing the items into the new buckets. Ideally the 819 | * items will distribute more or less evenly into the new buckets 820 | * (the extent to which this is true is a measure of the quality of 821 | * the hash function as it applies to the key domain). 822 | * 823 | * With the items distributed into more buckets, the chain length 824 | * (item count) in each bucket is reduced. Thus by expanding buckets 825 | * the hash keeps a bound on the chain length. This bounded chain 826 | * length is the essence of how a hash provides constant time lookup. 827 | * 828 | * The calculation of tbl->ideal_chain_maxlen below deserves some 829 | * explanation. First, keep in mind that we're calculating the ideal 830 | * maximum chain length based on the *new* (doubled) bucket count. 831 | * In fractions this is just n/b (n=number of items,b=new num buckets). 832 | * Since the ideal chain length is an integer, we want to calculate 833 | * ceil(n/b). We don't depend on floating point arithmetic in this 834 | * hash, so to calculate ceil(n/b) with integers we could write 835 | * 836 | * ceil(n/b) = (n/b) + ((n%b)?1:0) 837 | * 838 | * and in fact a previous version of this hash did just that. 839 | * But now we have improved things a bit by recognizing that b is 840 | * always a power of two. We keep its base 2 log handy (call it lb), 841 | * so now we can write this with a bit shift and logical AND: 842 | * 843 | * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) 844 | * 845 | */ 846 | #define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ 847 | do { \ 848 | unsigned _he_bkt; \ 849 | unsigned _he_bkt_i; \ 850 | struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ 851 | UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ 852 | _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ 853 | 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ 854 | if (!_he_new_buckets) { \ 855 | HASH_RECORD_OOM(oomed); \ 856 | } else { \ 857 | uthash_bzero(_he_new_buckets, \ 858 | 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ 859 | (tbl)->ideal_chain_maxlen = \ 860 | ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ 861 | ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ 862 | (tbl)->nonideal_items = 0; \ 863 | for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ 864 | _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ 865 | while (_he_thh != NULL) { \ 866 | _he_hh_nxt = _he_thh->hh_next; \ 867 | HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ 868 | _he_newbkt = &(_he_new_buckets[_he_bkt]); \ 869 | if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ 870 | (tbl)->nonideal_items++; \ 871 | if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ 872 | _he_newbkt->expand_mult++; \ 873 | } \ 874 | } \ 875 | _he_thh->hh_prev = NULL; \ 876 | _he_thh->hh_next = _he_newbkt->hh_head; \ 877 | if (_he_newbkt->hh_head != NULL) { \ 878 | _he_newbkt->hh_head->hh_prev = _he_thh; \ 879 | } \ 880 | _he_newbkt->hh_head = _he_thh; \ 881 | _he_thh = _he_hh_nxt; \ 882 | } \ 883 | } \ 884 | uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ 885 | (tbl)->num_buckets *= 2U; \ 886 | (tbl)->log2_num_buckets++; \ 887 | (tbl)->buckets = _he_new_buckets; \ 888 | (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ 889 | ((tbl)->ineff_expands+1U) : 0U; \ 890 | if ((tbl)->ineff_expands > 1U) { \ 891 | (tbl)->noexpand = 1; \ 892 | uthash_noexpand_fyi(tbl); \ 893 | } \ 894 | uthash_expand_fyi(tbl); \ 895 | } \ 896 | } while (0) 897 | 898 | 899 | /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ 900 | /* Note that HASH_SORT assumes the hash handle name to be hh. 901 | * HASH_SRT was added to allow the hash handle name to be passed in. */ 902 | #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) 903 | #define HASH_SRT(hh,head,cmpfcn) \ 904 | do { \ 905 | unsigned _hs_i; \ 906 | unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ 907 | struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ 908 | if (head != NULL) { \ 909 | _hs_insize = 1; \ 910 | _hs_looping = 1; \ 911 | _hs_list = &((head)->hh); \ 912 | while (_hs_looping != 0U) { \ 913 | _hs_p = _hs_list; \ 914 | _hs_list = NULL; \ 915 | _hs_tail = NULL; \ 916 | _hs_nmerges = 0; \ 917 | while (_hs_p != NULL) { \ 918 | _hs_nmerges++; \ 919 | _hs_q = _hs_p; \ 920 | _hs_psize = 0; \ 921 | for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ 922 | _hs_psize++; \ 923 | _hs_q = ((_hs_q->next != NULL) ? \ 924 | HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ 925 | if (_hs_q == NULL) { \ 926 | break; \ 927 | } \ 928 | } \ 929 | _hs_qsize = _hs_insize; \ 930 | while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ 931 | if (_hs_psize == 0U) { \ 932 | _hs_e = _hs_q; \ 933 | _hs_q = ((_hs_q->next != NULL) ? \ 934 | HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ 935 | _hs_qsize--; \ 936 | } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ 937 | _hs_e = _hs_p; \ 938 | if (_hs_p != NULL) { \ 939 | _hs_p = ((_hs_p->next != NULL) ? \ 940 | HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ 941 | } \ 942 | _hs_psize--; \ 943 | } else if ((cmpfcn( \ 944 | DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ 945 | DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ 946 | )) <= 0) { \ 947 | _hs_e = _hs_p; \ 948 | if (_hs_p != NULL) { \ 949 | _hs_p = ((_hs_p->next != NULL) ? \ 950 | HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ 951 | } \ 952 | _hs_psize--; \ 953 | } else { \ 954 | _hs_e = _hs_q; \ 955 | _hs_q = ((_hs_q->next != NULL) ? \ 956 | HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ 957 | _hs_qsize--; \ 958 | } \ 959 | if ( _hs_tail != NULL ) { \ 960 | _hs_tail->next = ((_hs_e != NULL) ? \ 961 | ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ 962 | } else { \ 963 | _hs_list = _hs_e; \ 964 | } \ 965 | if (_hs_e != NULL) { \ 966 | _hs_e->prev = ((_hs_tail != NULL) ? \ 967 | ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ 968 | } \ 969 | _hs_tail = _hs_e; \ 970 | } \ 971 | _hs_p = _hs_q; \ 972 | } \ 973 | if (_hs_tail != NULL) { \ 974 | _hs_tail->next = NULL; \ 975 | } \ 976 | if (_hs_nmerges <= 1U) { \ 977 | _hs_looping = 0; \ 978 | (head)->hh.tbl->tail = _hs_tail; \ 979 | DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ 980 | } \ 981 | _hs_insize *= 2U; \ 982 | } \ 983 | HASH_FSCK(hh, head, "HASH_SRT"); \ 984 | } \ 985 | } while (0) 986 | 987 | /* This function selects items from one hash into another hash. 988 | * The end result is that the selected items have dual presence 989 | * in both hashes. There is no copy of the items made; rather 990 | * they are added into the new hash through a secondary hash 991 | * hash handle that must be present in the structure. */ 992 | #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ 993 | do { \ 994 | unsigned _src_bkt, _dst_bkt; \ 995 | void *_last_elt = NULL, *_elt; \ 996 | UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ 997 | ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ 998 | if ((src) != NULL) { \ 999 | for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ 1000 | for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ 1001 | _src_hh != NULL; \ 1002 | _src_hh = _src_hh->hh_next) { \ 1003 | _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ 1004 | if (cond(_elt)) { \ 1005 | IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ 1006 | _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \ 1007 | _dst_hh->key = _src_hh->key; \ 1008 | _dst_hh->keylen = _src_hh->keylen; \ 1009 | _dst_hh->hashv = _src_hh->hashv; \ 1010 | _dst_hh->prev = _last_elt; \ 1011 | _dst_hh->next = NULL; \ 1012 | if (_last_elt_hh != NULL) { \ 1013 | _last_elt_hh->next = _elt; \ 1014 | } \ 1015 | if ((dst) == NULL) { \ 1016 | DECLTYPE_ASSIGN(dst, _elt); \ 1017 | HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ 1018 | IF_HASH_NONFATAL_OOM( \ 1019 | if (_hs_oomed) { \ 1020 | uthash_nonfatal_oom(_elt); \ 1021 | (dst) = NULL; \ 1022 | continue; \ 1023 | } \ 1024 | ) \ 1025 | } else { \ 1026 | _dst_hh->tbl = (dst)->hh_dst.tbl; \ 1027 | } \ 1028 | HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ 1029 | HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ 1030 | (dst)->hh_dst.tbl->num_items++; \ 1031 | IF_HASH_NONFATAL_OOM( \ 1032 | if (_hs_oomed) { \ 1033 | HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ 1034 | HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ 1035 | _dst_hh->tbl = NULL; \ 1036 | uthash_nonfatal_oom(_elt); \ 1037 | continue; \ 1038 | } \ 1039 | ) \ 1040 | HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ 1041 | _last_elt = _elt; \ 1042 | _last_elt_hh = _dst_hh; \ 1043 | } \ 1044 | } \ 1045 | } \ 1046 | } \ 1047 | HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ 1048 | } while (0) 1049 | 1050 | #define HASH_CLEAR(hh,head) \ 1051 | do { \ 1052 | if ((head) != NULL) { \ 1053 | HASH_BLOOM_FREE((head)->hh.tbl); \ 1054 | uthash_free((head)->hh.tbl->buckets, \ 1055 | (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ 1056 | uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ 1057 | (head) = NULL; \ 1058 | } \ 1059 | } while (0) 1060 | 1061 | #define HASH_OVERHEAD(hh,head) \ 1062 | (((head) != NULL) ? ( \ 1063 | (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ 1064 | ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ 1065 | sizeof(UT_hash_table) + \ 1066 | (HASH_BLOOM_BYTELEN))) : 0U) 1067 | 1068 | #ifdef NO_DECLTYPE 1069 | #define HASH_ITER(hh,head,el,tmp) \ 1070 | for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ 1071 | (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) 1072 | #else 1073 | #define HASH_ITER(hh,head,el,tmp) \ 1074 | for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ 1075 | (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) 1076 | #endif 1077 | 1078 | /* obtain a count of items in the hash */ 1079 | #define HASH_COUNT(head) HASH_CNT(hh,head) 1080 | #define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) 1081 | 1082 | typedef struct UT_hash_bucket { 1083 | struct UT_hash_handle *hh_head; 1084 | unsigned count; 1085 | 1086 | /* expand_mult is normally set to 0. In this situation, the max chain length 1087 | * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If 1088 | * the bucket's chain exceeds this length, bucket expansion is triggered). 1089 | * However, setting expand_mult to a non-zero value delays bucket expansion 1090 | * (that would be triggered by additions to this particular bucket) 1091 | * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. 1092 | * (The multiplier is simply expand_mult+1). The whole idea of this 1093 | * multiplier is to reduce bucket expansions, since they are expensive, in 1094 | * situations where we know that a particular bucket tends to be overused. 1095 | * It is better to let its chain length grow to a longer yet-still-bounded 1096 | * value, than to do an O(n) bucket expansion too often. 1097 | */ 1098 | unsigned expand_mult; 1099 | 1100 | } UT_hash_bucket; 1101 | 1102 | /* random signature used only to find hash tables in external analysis */ 1103 | #define HASH_SIGNATURE 0xa0111fe1u 1104 | #define HASH_BLOOM_SIGNATURE 0xb12220f2u 1105 | 1106 | typedef struct UT_hash_table { 1107 | UT_hash_bucket *buckets; 1108 | unsigned num_buckets, log2_num_buckets; 1109 | unsigned num_items; 1110 | struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ 1111 | ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ 1112 | 1113 | /* in an ideal situation (all buckets used equally), no bucket would have 1114 | * more than ceil(#items/#buckets) items. that's the ideal chain length. */ 1115 | unsigned ideal_chain_maxlen; 1116 | 1117 | /* nonideal_items is the number of items in the hash whose chain position 1118 | * exceeds the ideal chain maxlen. these items pay the penalty for an uneven 1119 | * hash distribution; reaching them in a chain traversal takes >ideal steps */ 1120 | unsigned nonideal_items; 1121 | 1122 | /* ineffective expands occur when a bucket doubling was performed, but 1123 | * afterward, more than half the items in the hash had nonideal chain 1124 | * positions. If this happens on two consecutive expansions we inhibit any 1125 | * further expansion, as it's not helping; this happens when the hash 1126 | * function isn't a good fit for the key domain. When expansion is inhibited 1127 | * the hash will still work, albeit no longer in constant time. */ 1128 | unsigned ineff_expands, noexpand; 1129 | 1130 | uint32_t signature; /* used only to find hash tables in external analysis */ 1131 | #ifdef HASH_BLOOM 1132 | uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ 1133 | uint8_t *bloom_bv; 1134 | uint8_t bloom_nbits; 1135 | #endif 1136 | 1137 | } UT_hash_table; 1138 | 1139 | typedef struct UT_hash_handle { 1140 | struct UT_hash_table *tbl; 1141 | void *prev; /* prev element in app order */ 1142 | void *next; /* next element in app order */ 1143 | struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ 1144 | struct UT_hash_handle *hh_next; /* next hh in bucket order */ 1145 | void *key; /* ptr to enclosing struct's key */ 1146 | unsigned keylen; /* enclosing struct's key len */ 1147 | unsigned hashv; /* result of hash-fcn(key) */ 1148 | } UT_hash_handle; 1149 | 1150 | #endif /* UTHASH_H */ 1151 | --------------------------------------------------------------------------------