In addition to the previous instructions, please check PRs for style.
- maximum text width is 80 characters
- indentation is 4 spaces
- no tabs, no trailing spaces
- list elements on the same line are separated with spaces
- hexadecimal literals are lowercase
- file names, function and type names, and global variables have the njs_ prefix
size_t
njs_utf8_length(u_char *p, size_t n)
{
u_char c, *last;
size_t len;
last = p + n;
for (len = 0; p < last; len++) {
c = *p;
if (c < 0x80) {
p++;
continue;
}
if (njs_utf8_decode(&p, last - p) > 0x10ffff) {
/* invalid UTF-8 */
return n;
}
}
return len;
}
A typical source file may contain the following sections separated by two empty lines:
- copyright statements
- includes
- preprocessor definitions
- type definitions
- function prototypes
- variable definitions
- function definitions
Copyright statements look like this:
/*
* Copyright (C) Author Name
* Copyright (C) Organization, Inc.
*/
If the file is modified significantly, the list of authors should be updated, the new author is added to the top.
The njs_main.h file is always included first. Then follow optional external header files:
#include <njs_main.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxslt/xslt.h>
#if (NJS_HAVE_EXSLT)
#include <libexslt/exslt.h>
#endifHeader files should include the so called "header protection":
#ifndef _NJS_VM_H_INCLUDED_
#define _NJS_VM_H_INCLUDED_
...
#endif /* _NJS_VM_H_INCLUDED_ */- "//" comments are not used
- text is written in English, American spelling is preferred
- multi-line comments are formatted like this:
/*
* The red-black tree code is based on the algorithm described in
* the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.
*/
/* find the server configuration for the address:port */Macro names start from njs_ or NJS_ prefix. Macro names for constants are uppercase. Parameterized macros and macros for initializers are lowercase. The macro name and value are separated by at least two spaces:
#define NJS_STRING_MAX_LENGTH 0x7fffffff
#define njs_queue_is_empty(queue) ((queue) == (queue)->prev)
#define njs_queue_insert_head(queue, link) \
(link)->next = (queue)->next; \
(link)->next->prev = (link); \
(link)->prev = (queue); \
(queue)->next = (link)
#define njs_null_string { 0, NULL }Conditions are inside parentheses, negation is outside:
#if (NJS_HAVE_OPENSSL)
...
#elif (NJS_HAVE_QUICKJS)
...
#elif (NJS_HAVE_ADDR2LINE)
...
#else /* default */
...
#endif /* NJS_HAVE_OPENSSL */Type names end with the "_t" suffix. A defined type name is separated by at least two spaces:
typedef uint8_t njs_vmcode_t;Structure types are defined using typedef. Inside structures, member types and names are aligned:
typedef struct {
size_t length;
u_char *start;
} njs_str_t;Keep alignment identical among different structures in the file. A structure that points to itself has the name, ending with "_s". Adjacent structure definitions are separated with two empty lines:
typedef struct njs_queue_link_s njs_queue_link_t;
struct njs_queue_link_s {
njs_queue_link_t *prev;
njs_queue_link_t *next;
};
typedef struct {
njs_queue_link_t head;
} njs_queue_t;Each structure member is declared on its own line:
typedef struct {
njs_uint_t hash;
njs_str_t key;
njs_str_t value;
u_char *lowcase_key;
} njs_table_elt_t;Function pointers inside structures have defined types ending with "_pt":
typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size);
typedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
typedef ssize_t (*ngx_send_pt)(ngx_connection_t *c, u_char *buf, size_t size);
typedef ngx_chain_t *(*ngx_send_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
typedef struct {
ngx_recv_pt recv;
ngx_recv_chain_pt recv_chain;
ngx_recv_pt udp_recv;
ngx_send_pt send;
ngx_send_pt udp_send;
ngx_send_chain_pt udp_send_chain;
ngx_send_chain_pt send_chain;
ngx_uint_t flags;
} ngx_os_io_t;Enumerations have types ending with "_t":
typedef enum {
NJS_NULL,
NJS_UNDEFINED,
NJS_BOOLEAN,
NJS_NUMBER,
NJS_STRING,
NJS_SYMBOL
} njs_value_type_t;Variables are declared sorted by length of a base type, then alphabetically. Type names and variable names are aligned. The type and name "columns" are separated with two spaces. Large arrays are put at the end of a declaration block:
u_char *rv, *p;
ngx_conf_t *cf;
ngx_uint_t i, j, k;
unsigned int len;
struct sockaddr *sa;
const unsigned char *data;
ngx_peer_connection_t *pc;
ngx_http_core_srv_conf_t **cscfp;
ngx_http_upstream_srv_conf_t *us, *uscf;
u_char text[NGX_SOCKADDR_STRLEN];Static and global variables may be initialized on declaration:
static ngx_str_t ngx_http_memcached_key = ngx_string("memcached_key");
static ngx_uint_t mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static uint32_t ngx_crc32_table16[] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
...
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};There is a bunch of commonly used type/name combinations:
u_char *rv;
ngx_int_t rc;
ngx_conf_t *cf;
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_peer_connection_t *pc;
ngx_http_upstream_srv_conf_t *us, *uscf;All functions (even static ones) should have prototypes. Prototypes include argument names. Long prototypes are wrapped with a single indentation on continuation lines:
static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_init_phases(ngx_conf_t *cf,
ngx_http_core_main_conf_t *cmcf);
static char *ngx_http_merge_servers(ngx_conf_t *cf,
ngx_http_core_main_conf_t *cmcf, ngx_http_module_t *module,
ngx_uint_t ctx_index);The function name in a definition starts with a new line. The function body opening and closing braces are on separate lines. The body of a function is indented. There are two empty lines between functions:
static ngx_int_t
ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len)
{
...
}
static ngx_int_t
ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt)
{
...
}There is no space after the function name and opening parenthesis. Long function calls are wrapped such that continuation lines start from the position of the first function argument. If this is impossible, format the first continuation line such that it ends at position 79:
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http header: \"%V: %V\"",
&h->key, &h->value);
hc->busy = ngx_palloc(r->connection->pool,
cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));The ngx_inline macro should be used instead of inline:
static ngx_inline void ngx_cpuid(uint32_t i, uint32_t *buf);Binary operators except "." and "−>" should be separated from their operands by one space. Unary operators and subscripts are not separated from their operands by spaces:
width = width * 10 + (*fmt++ - '0');
ch = (u_char) ((decoded << 4) + (ch - '0'));
r->exten.data = &r->uri.data[i + 1];Type casts are separated by one space from casted expressions. An asterisk inside type cast is separated with space from type name:
len = ngx_sock_ntop((struct sockaddr *) sin6, p, len, 1);If an expression does not fit into single line, it is wrapped. The preferred point to break a line is a binary operator. The continuation line is lined up with the start of expression:
if (status == NGX_HTTP_MOVED_PERMANENTLY
|| status == NGX_HTTP_MOVED_TEMPORARILY
|| status == NGX_HTTP_SEE_OTHER
|| status == NGX_HTTP_TEMPORARY_REDIRECT
|| status == NGX_HTTP_PERMANENT_REDIRECT)
{
...
}
p->temp_file->warn = "an upstream response is buffered "
"to a temporary file";As a last resort, it is possible to wrap an expression so that the continuation line ends at position 79:
hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)
+ size * sizeof(ngx_hash_elt_t *));The above rules also apply to sub-expressions, where each sub-expression has its own indentation level:
if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)
|| c->stale_updating) && !r->background
&& u->conf->cache_background_update)
{
...
}Sometimes, it is convenient to wrap an expression after a cast. In this case, the continuation line is indented:
node = (ngx_rbtree_node_t *)
((u_char *) lr - offsetof(ngx_rbtree_node_t, color));Pointers are explicitly compared to NULL (not 0):
if (ptr != NULL) {
...
}The "if" keyword is separated from the condition by one space. Opening brace is located on the same line, or on a dedicated line if the condition takes several lines. Closing brace is located on a dedicated line, optionally followed by "else if / else". Usually, there is an empty line before the "else if / else" part:
if (node->left == sentinel) {
temp = node->right;
subst = node;
} else if (node->right == sentinel) {
temp = node->left;
subst = node;
} else {
subst = ngx_rbtree_min(node->right, sentinel);
if (subst->left != sentinel) {
temp = subst->left;
} else {
temp = subst->right;
}
}Similar formatting rules are applied to "do" and "while" loops:
while (p < last && *p == ' ') {
p++;
}
do {
ctx->node = rn;
ctx = ctx->next;
} while (ctx);The "switch" keyword is separated from the condition by one space. Opening brace is located on the same line. Closing brace is located on a dedicated line. The "case" keywords are lined up with "switch":
switch (ch) {
case '!':
looked = 2;
state = ssi_comment0_state;
break;
case '<':
copy_end = p;
break;
default:
copy_end = p;
looked = 0;
state = ssi_start_state;
break;
}Most "for" loops are formatted like this:
for (i = 0; i < ccf->env.nelts; i++) {
...
}
for (q = ngx_queue_head(locations);
q != ngx_queue_sentinel(locations);
q = ngx_queue_next(q))
{
...
}If some part of the "for" statement is omitted, this is indicated by the "/* void */" comment:
for (i = 0; /* void */ ; i++) {
...
}A loop with an empty body is also indicated by the "/* void */" comment which may be put on the same line:
for (cl = *busy; cl->next; cl = cl->next) { /* void */ }An endless loop looks like this:
for ( ;; ) {
...
}Labels are surrounded with empty lines and are indented at the previous level:
if (i == 0) {
u->err = "host not found";
goto failed;
}
u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t));
if (u->addrs == NULL) {
goto failed;
}
u->naddrs = i;
...
return NGX_OK;
failed:
freeaddrinfo(res);
return NGX_ERROR;