<commit_before>// Scintilla source code edit control
/** @file LexLua.cxx
 ** Lexer for Lua language.
 **
 ** Written by Paul Winwood.
 ** Folder by Alexey Yutkin.
 ** Modified by Marcos E. Wurzius & Philippe Lhoste
 **/

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

// Extended to accept accented characters
static inline bool IsAWordChar(int ch) {
	return ch >= 0x80 ||
	       (isalnum(ch) || ch == '.' || ch == '_');
}

static inline bool IsAWordStart(int ch) {
	return ch >= 0x80 ||
	       (isalpha(ch) || ch == '_');
}

static inline bool IsANumberChar(int ch) {
	// Not exactly following number definition (several dots are seen as OK, etc.)
	// but probably enough in most cases.
	return (ch < 0x80) &&
	        (isdigit(ch) || toupper(ch) == 'E' ||
             ch == '.' || ch == '-' || ch == '+');
}

static inline bool IsLuaOperator(int ch) {
	if (ch >= 0x80 || isalnum(ch)) {
		return false;
	}
	// '.' left out as it is used to make up numbers
	if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
		ch == '(' || ch == ')' || ch == '=' ||
		ch == '{' || ch == '}' || ch == '~' ||
		ch == '[' || ch == ']' || ch == ';' ||
		ch == '<' || ch == '>' || ch == ',' ||
		ch == '.' || ch == '^' || ch == '%' || ch == ':') {
		return true;
	}
	return false;
}

static void ColouriseLuaDoc(
	unsigned int startPos,
	int length,
	int initStyle,
	WordList *keywordlists[],
	Accessor &styler) {

	WordList &keywords = *keywordlists[0];
	WordList &keywords2 = *keywordlists[1];
	WordList &keywords3 = *keywordlists[2];
	WordList &keywords4 = *keywordlists[3];
	WordList &keywords5 = *keywordlists[4];
	WordList &keywords6 = *keywordlists[5];
	WordList &keywords7 = *keywordlists[6];
	WordList &keywords8 = *keywordlists[7];

	int currentLine = styler.GetLine(startPos);
	// Initialize the literal string [[ ... ]] nesting level, if we are inside such a string.
	int literalStringLevel = 0;
	if (initStyle == SCE_LUA_LITERALSTRING) {
		literalStringLevel = styler.GetLineState(currentLine - 1);
	}
	// Initialize the block comment --[[ ... ]] nesting level, if we are inside such a comment
	int blockCommentLevel = 0;
	if (initStyle == SCE_LUA_COMMENT) {
		blockCommentLevel = styler.GetLineState(currentLine - 1);
	}

	// Do not leak onto next line
	if (initStyle == SCE_LUA_STRINGEOL || initStyle == SCE_LUA_COMMENTLINE || initStyle == SCE_LUA_PREPROCESSOR) {
		initStyle = SCE_LUA_DEFAULT;
	}

	StyleContext sc(startPos, length, initStyle, styler);
	if (startPos == 0 && sc.ch == '#') {
		// shbang line: # is a comment only if first char of the script
		sc.SetState(SCE_LUA_COMMENTLINE);
	}
	for (; sc.More(); sc.Forward()) {
		if (sc.atLineEnd) {
			// Update the line state, so it can be seen by next line
			currentLine = styler.GetLine(sc.currentPos);
			switch (sc.state) {
			case SCE_LUA_LITERALSTRING:
				// Inside a literal string, we set the line state
				styler.SetLineState(currentLine, literalStringLevel);
				break;
			case SCE_LUA_COMMENT: 	// Block comment
				// Inside a block comment, we set the line state
				styler.SetLineState(currentLine, blockCommentLevel);
				break;
			default:
				// Reset the line state
				styler.SetLineState(currentLine, 0);
				break;
			}
		}
		if (sc.atLineStart && (sc.state == SCE_LUA_STRING)) {
			// Prevent SCE_LUA_STRINGEOL from leaking back to previous line
			sc.SetState(SCE_LUA_STRING);
		}

		// Handle string line continuation
		if ((sc.state == SCE_LUA_STRING || sc.state == SCE_LUA_CHARACTER) &&
		        sc.ch == '\\') {
			if (sc.chNext == '\n' || sc.chNext == '\r') {
				sc.Forward();
				if (sc.ch == '\r' && sc.chNext == '\n') {
					sc.Forward();
				}
				continue;
			}
		}

		// Determine if the current state should terminate.
		if (sc.state == SCE_LUA_OPERATOR) {
			sc.SetState(SCE_LUA_DEFAULT);
		} else if (sc.state == SCE_LUA_NUMBER) {
			// We stop the number definition on non-numerical non-dot non-eE non-sign char
			if (!IsANumberChar(sc.ch)) {
				sc.SetState(SCE_LUA_DEFAULT);
			}
		} else if (sc.state == SCE_LUA_IDENTIFIER) {
			if (!IsAWordChar(sc.ch) || sc.Match('.', '.')) {
				char s[100];
				sc.GetCurrent(s, sizeof(s));
				if (keywords.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD);
				} else if (keywords2.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD2);
				} else if (keywords3.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD3);
				} else if (keywords4.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD4);
				} else if (keywords5.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD5);
				} else if (keywords6.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD6);
				} else if (keywords6.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD6);
				} else if (keywords7.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD7);
				} else if (keywords8.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD8);
				}
				sc.SetState(SCE_LUA_DEFAULT);
			}
		} else if (sc.state == SCE_LUA_COMMENTLINE) {
			if (sc.atLineEnd) {
				sc.ForwardSetState(SCE_LUA_DEFAULT);
			}
		} else if (sc.state == SCE_LUA_PREPROCESSOR) {
			if (sc.atLineEnd) {
				sc.ForwardSetState(SCE_LUA_DEFAULT);
			}
		} else if (sc.state == SCE_LUA_STRING) {
			if (sc.ch == '\\') {
				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
					sc.Forward();
				}
			} else if (sc.ch == '\"') {
				sc.ForwardSetState(SCE_LUA_DEFAULT);
			} else if (sc.atLineEnd) {
				sc.ChangeState(SCE_LUA_STRINGEOL);
				sc.ForwardSetState(SCE_LUA_DEFAULT);
			}
		} else if (sc.state == SCE_LUA_CHARACTER) {
			if (sc.ch == '\\') {
				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
					sc.Forward();
				}
			} else if (sc.ch == '\'') {
				sc.ForwardSetState(SCE_LUA_DEFAULT);
			} else if (sc.atLineEnd) {
				sc.ChangeState(SCE_LUA_STRINGEOL);
				sc.ForwardSetState(SCE_LUA_DEFAULT);
			}
		} else if (sc.state == SCE_LUA_LITERALSTRING) {
			if (sc.Match('[', '[')) {
				literalStringLevel++;
				sc.Forward();
				sc.SetState(SCE_LUA_LITERALSTRING);
			} else if (sc.Match(']', ']') && literalStringLevel > 0) {
				literalStringLevel--;
				sc.Forward();
				if (literalStringLevel == 0) {
					sc.ForwardSetState(SCE_LUA_DEFAULT);
				}
			}
		} else if (sc.state == SCE_LUA_COMMENT) {	// Lua 5.0's block comment
			if (sc.Match('[', '[')) {
				blockCommentLevel++;
				sc.Forward();
			} else if (sc.Match(']', ']') && blockCommentLevel > 0) {
				blockCommentLevel--;
				sc.Forward();
				if (blockCommentLevel == 0) {
					sc.ForwardSetState(SCE_LUA_DEFAULT);
				}
			}
		}

		// Determine if a new state should be entered.
		if (sc.state == SCE_LUA_DEFAULT) {
			if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
				sc.SetState(SCE_LUA_NUMBER);
			} else if (IsAWordStart(sc.ch)) {
				sc.SetState(SCE_LUA_IDENTIFIER);
			} else if (sc.Match('\"')) {
				sc.SetState(SCE_LUA_STRING);
			} else if (sc.Match('\'')) {
				sc.SetState(SCE_LUA_CHARACTER);
			} else if (sc.Match('[', '[')) {
				literalStringLevel = 1;
				sc.SetState(SCE_LUA_LITERALSTRING);
				sc.Forward();
			} else if (sc.Match("--[[")) {	// Lua 5.0's block comment
				blockCommentLevel = 1;
				sc.SetState(SCE_LUA_COMMENT);
				sc.Forward(3);
			} else if (sc.Match('-', '-')) {
				sc.SetState(SCE_LUA_COMMENTLINE);
				sc.Forward();
			} else if (sc.atLineStart && sc.Match('$')) {
				sc.SetState(SCE_LUA_PREPROCESSOR);	// Obsolete since Lua 4.0, but still in old code
			} else if (IsLuaOperator(static_cast<char>(sc.ch))) {
				sc.SetState(SCE_LUA_OPERATOR);
			}
		}
	}
	sc.Complete();
}

static void FoldLuaDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
                       Accessor &styler) {
	unsigned int lengthDoc = startPos + length;
	int visibleChars = 0;
	int lineCurrent = styler.GetLine(startPos);
	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
	int levelCurrent = levelPrev;
	char chNext = styler[startPos];
	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
	int styleNext = styler.StyleAt(startPos);
	char s[10];

	for (unsigned int i = startPos; i < lengthDoc; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);
		int style = styleNext;
		styleNext = styler.StyleAt(i + 1);
		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
		if (style == SCE_LUA_WORD) {
			if (ch == 'i' || ch == 'd' || ch == 'f' || ch == 'e' || ch == 'r' || ch == 'u') {
				for (unsigned int j = 0; j < 8; j++) {
					if (!iswordchar(styler[i + j])) {
						break;
					}
					s[j] = styler[i + j];
					s[j + 1] = '\0';
				}

				if ((strcmp(s, "if") == 0) || (strcmp(s, "do") == 0) || (strcmp(s, "function") == 0) || (strcmp(s, "repeat") == 0)) {
					levelCurrent++;
				}
				if ((strcmp(s, "end") == 0) || (strcmp(s, "elseif") == 0) || (strcmp(s, "until") == 0)) {
					levelCurrent--;
				}
			}
		} else if (style == SCE_LUA_OPERATOR) {
			if (ch == '{' || ch == '(') {
				levelCurrent++;
			} else if (ch == '}' || ch == ')') {
				levelCurrent--;
			}
		} else if (style == SCE_LUA_LITERALSTRING || style == SCE_LUA_COMMENT) {
			if (ch == '[') {
				levelCurrent++;
			} else if (ch == ']') {
				levelCurrent--;
			}
		}

		if (atEOL) {
			int lev = levelPrev;
			if (visibleChars == 0 && foldCompact) {
				lev |= SC_FOLDLEVELWHITEFLAG;
			}
			if ((levelCurrent > levelPrev) && (visibleChars > 0)) {
				lev |= SC_FOLDLEVELHEADERFLAG;
			}
			if (lev != styler.LevelAt(lineCurrent)) {
				styler.SetLevel(lineCurrent, lev);
			}
			lineCurrent++;
			levelPrev = levelCurrent;
			visibleChars = 0;
		}
		if (!isspacechar(ch)) {
			visibleChars++;
		}
	}
	// Fill in the real level of the next line, keeping the current flags as they will be filled in later

	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
}

static const char * const luaWordListDesc[] = {
	"Keywords",
	"Basic functions",
	"String, (table) & math functions",
	"(coroutines), I/O & system facilities",
	"user1",
	"user2",
	"user3",
	"user4",
	0
};

LexerModule lmLua(SCLEX_LUA, ColouriseLuaDoc, "lua", FoldLuaDoc, luaWordListDesc);
<commit_msg>Patch from Kein-Hong Man support new Lua 5.1 long string and block comment syntax [=[...]=] and --[=[...]=].<commit_after>// Scintilla source code edit control
/** @file LexLua.cxx
 ** Lexer for Lua language.
 **
 ** Written by Paul Winwood.
 ** Folder by Alexey Yutkin.
 ** Modified by Marcos E. Wurzius & Philippe Lhoste
 **/

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

// Extended to accept accented characters
static inline bool IsAWordChar(int ch) {
	return ch >= 0x80 ||
	       (isalnum(ch) || ch == '.' || ch == '_');
}

static inline bool IsAWordStart(int ch) {
	return ch >= 0x80 ||
	       (isalpha(ch) || ch == '_');
}

static inline bool IsANumberChar(int ch) {
	// Not exactly following number definition (several dots are seen as OK, etc.)
	// but probably enough in most cases.
	return (ch < 0x80) &&
	        (isdigit(ch) || toupper(ch) == 'E' ||
             ch == '.' || ch == '-' || ch == '+');
}

static inline bool IsLuaOperator(int ch) {
	if (ch >= 0x80 || isalnum(ch)) {
		return false;
	}
	// '.' left out as it is used to make up numbers
	if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
		ch == '(' || ch == ')' || ch == '=' ||
		ch == '{' || ch == '}' || ch == '~' ||
		ch == '[' || ch == ']' || ch == ';' ||
		ch == '<' || ch == '>' || ch == ',' ||
		ch == '.' || ch == '^' || ch == '%' || ch == ':') {
		return true;
	}
	return false;
}

// Test for [=[ ... ]=] delimiters, returns 0 if it's only a [ or ],
// return 1 for [[ or ]], returns >=2 for [=[ or ]=] and so on.
// The maximum number of '=' characters allowed is 254.
static int LongDelimCheck(StyleContext &sc) {
	int sep = 1;
	while (sc.GetRelative(sep) == '=' && sep < 0xFF)
		sep++;
	if (sc.GetRelative(sep) == sc.ch)
		return sep;
	return 0;
}

static void ColouriseLuaDoc(
	unsigned int startPos,
	int length,
	int initStyle,
	WordList *keywordlists[],
	Accessor &styler) {

	WordList &keywords = *keywordlists[0];
	WordList &keywords2 = *keywordlists[1];
	WordList &keywords3 = *keywordlists[2];
	WordList &keywords4 = *keywordlists[3];
	WordList &keywords5 = *keywordlists[4];
	WordList &keywords6 = *keywordlists[5];
	WordList &keywords7 = *keywordlists[6];
	WordList &keywords8 = *keywordlists[7];

	int currentLine = styler.GetLine(startPos);
	// Initialize long string [[ ... ]] or block comment --[[ ... ]] nesting level,
	// if we are inside such a string. Block comment was introduced in Lua 5.0,
	// blocks with separators [=[ ... ]=] in Lua 5.1.
	int nestLevel = 0;
	int sepCount = 0;
	if (initStyle == SCE_LUA_LITERALSTRING || initStyle == SCE_LUA_COMMENT) {
		int lineState = styler.GetLineState(currentLine - 1);
		nestLevel = lineState >> 8;
		sepCount = lineState & 0xFF;
	}

	// Do not leak onto next line
	if (initStyle == SCE_LUA_STRINGEOL || initStyle == SCE_LUA_COMMENTLINE || initStyle == SCE_LUA_PREPROCESSOR) {
		initStyle = SCE_LUA_DEFAULT;
	}

	StyleContext sc(startPos, length, initStyle, styler);
	if (startPos == 0 && sc.ch == '#') {
		// shbang line: # is a comment only if first char of the script
		sc.SetState(SCE_LUA_COMMENTLINE);
	}
	for (; sc.More(); sc.Forward()) {
		if (sc.atLineEnd) {
			// Update the line state, so it can be seen by next line
			currentLine = styler.GetLine(sc.currentPos);
			switch (sc.state) {
			case SCE_LUA_LITERALSTRING:
			case SCE_LUA_COMMENT:
				// Inside a literal string or block comment, we set the line state
				styler.SetLineState(currentLine, (nestLevel << 8) | sepCount);
				break;
			default:
				// Reset the line state
				styler.SetLineState(currentLine, 0);
				break;
			}
		}
		if (sc.atLineStart && (sc.state == SCE_LUA_STRING)) {
			// Prevent SCE_LUA_STRINGEOL from leaking back to previous line
			sc.SetState(SCE_LUA_STRING);
		}

		// Handle string line continuation
		if ((sc.state == SCE_LUA_STRING || sc.state == SCE_LUA_CHARACTER) &&
		        sc.ch == '\\') {
			if (sc.chNext == '\n' || sc.chNext == '\r') {
				sc.Forward();
				if (sc.ch == '\r' && sc.chNext == '\n') {
					sc.Forward();
				}
				continue;
			}
		}

		// Determine if the current state should terminate.
		if (sc.state == SCE_LUA_OPERATOR) {
			sc.SetState(SCE_LUA_DEFAULT);
		} else if (sc.state == SCE_LUA_NUMBER) {
			// We stop the number definition on non-numerical non-dot non-eE non-sign char
			if (!IsANumberChar(sc.ch)) {
				sc.SetState(SCE_LUA_DEFAULT);
			}
		} else if (sc.state == SCE_LUA_IDENTIFIER) {
			if (!IsAWordChar(sc.ch) || sc.Match('.', '.')) {
				char s[100];
				sc.GetCurrent(s, sizeof(s));
				if (keywords.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD);
				} else if (keywords2.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD2);
				} else if (keywords3.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD3);
				} else if (keywords4.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD4);
				} else if (keywords5.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD5);
				} else if (keywords6.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD6);
				} else if (keywords6.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD6);
				} else if (keywords7.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD7);
				} else if (keywords8.InList(s)) {
					sc.ChangeState(SCE_LUA_WORD8);
				}
				sc.SetState(SCE_LUA_DEFAULT);
			}
		} else if (sc.state == SCE_LUA_COMMENTLINE || sc.state == SCE_LUA_PREPROCESSOR) {
			if (sc.atLineEnd) {
				sc.ForwardSetState(SCE_LUA_DEFAULT);
			}
		} else if (sc.state == SCE_LUA_STRING) {
			if (sc.ch == '\\') {
				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
					sc.Forward();
				}
			} else if (sc.ch == '\"') {
				sc.ForwardSetState(SCE_LUA_DEFAULT);
			} else if (sc.atLineEnd) {
				sc.ChangeState(SCE_LUA_STRINGEOL);
				sc.ForwardSetState(SCE_LUA_DEFAULT);
			}
		} else if (sc.state == SCE_LUA_CHARACTER) {
			if (sc.ch == '\\') {
				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
					sc.Forward();
				}
			} else if (sc.ch == '\'') {
				sc.ForwardSetState(SCE_LUA_DEFAULT);
			} else if (sc.atLineEnd) {
				sc.ChangeState(SCE_LUA_STRINGEOL);
				sc.ForwardSetState(SCE_LUA_DEFAULT);
			}
		} else if (sc.state == SCE_LUA_LITERALSTRING || sc.state == SCE_LUA_COMMENT) {
			if (sc.ch == '[') {
				int sep = LongDelimCheck(sc);
				if (sep == 1 && sepCount == 1) {    // [[-only allowed to nest
					nestLevel++;
					sc.Forward();
				}
			} else if (sc.ch == ']') {
				int sep = LongDelimCheck(sc);
				if (sep == 1 && sepCount == 1) {    // un-nest with ]]-only
					nestLevel--;
					sc.Forward();
					if (nestLevel == 0) {
						sc.ForwardSetState(SCE_LUA_DEFAULT);
					}
				} else if (sep > 1 && sep == sepCount) {   // ]=]-style delim
					sc.Forward(sep);
					sc.ForwardSetState(SCE_LUA_DEFAULT);
				}
			}
		}

		// Determine if a new state should be entered.
		if (sc.state == SCE_LUA_DEFAULT) {
			if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
				sc.SetState(SCE_LUA_NUMBER);
			} else if (IsAWordStart(sc.ch)) {
				sc.SetState(SCE_LUA_IDENTIFIER);
			} else if (sc.ch == '\"') {
				sc.SetState(SCE_LUA_STRING);
			} else if (sc.ch == '\'') {
				sc.SetState(SCE_LUA_CHARACTER);
			} else if (sc.ch == '[') {
				sepCount = LongDelimCheck(sc);
				if (sepCount == 0) {
					sc.SetState(SCE_LUA_OPERATOR);
				} else {
					nestLevel = 1;
					sc.SetState(SCE_LUA_LITERALSTRING);
					sc.Forward(sepCount);
				}
			} else if (sc.Match('-', '-')) {
				sc.SetState(SCE_LUA_COMMENTLINE);
				if (sc.Match("--[")) {
					sc.Forward(2);
					sepCount = LongDelimCheck(sc);
					if (sepCount > 0) {
						nestLevel = 1;
						sc.ChangeState(SCE_LUA_COMMENT);
						sc.Forward(sepCount);
					}
				} else {
					sc.Forward();
				}
			} else if (sc.atLineStart && sc.Match('$')) {
				sc.SetState(SCE_LUA_PREPROCESSOR);	// Obsolete since Lua 4.0, but still in old code
			} else if (IsLuaOperator(static_cast<char>(sc.ch))) {
				sc.SetState(SCE_LUA_OPERATOR);
			}
		}
	}
	sc.Complete();
}

static void FoldLuaDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
                       Accessor &styler) {
	unsigned int lengthDoc = startPos + length;
	int visibleChars = 0;
	int lineCurrent = styler.GetLine(startPos);
	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
	int levelCurrent = levelPrev;
	char chNext = styler[startPos];
	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
	int styleNext = styler.StyleAt(startPos);
	char s[10];

	for (unsigned int i = startPos; i < lengthDoc; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);
		int style = styleNext;
		styleNext = styler.StyleAt(i + 1);
		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
		if (style == SCE_LUA_WORD) {
			if (ch == 'i' || ch == 'd' || ch == 'f' || ch == 'e' || ch == 'r' || ch == 'u') {
				for (unsigned int j = 0; j < 8; j++) {
					if (!iswordchar(styler[i + j])) {
						break;
					}
					s[j] = styler[i + j];
					s[j + 1] = '\0';
				}

				if ((strcmp(s, "if") == 0) || (strcmp(s, "do") == 0) || (strcmp(s, "function") == 0) || (strcmp(s, "repeat") == 0)) {
					levelCurrent++;
				}
				if ((strcmp(s, "end") == 0) || (strcmp(s, "elseif") == 0) || (strcmp(s, "until") == 0)) {
					levelCurrent--;
				}
			}
		} else if (style == SCE_LUA_OPERATOR) {
			if (ch == '{' || ch == '(') {
				levelCurrent++;
			} else if (ch == '}' || ch == ')') {
				levelCurrent--;
			}
		} else if (style == SCE_LUA_LITERALSTRING || style == SCE_LUA_COMMENT) {
			if (ch == '[') {
				levelCurrent++;
			} else if (ch == ']') {
				levelCurrent--;
			}
		}

		if (atEOL) {
			int lev = levelPrev;
			if (visibleChars == 0 && foldCompact) {
				lev |= SC_FOLDLEVELWHITEFLAG;
			}
			if ((levelCurrent > levelPrev) && (visibleChars > 0)) {
				lev |= SC_FOLDLEVELHEADERFLAG;
			}
			if (lev != styler.LevelAt(lineCurrent)) {
				styler.SetLevel(lineCurrent, lev);
			}
			lineCurrent++;
			levelPrev = levelCurrent;
			visibleChars = 0;
		}
		if (!isspacechar(ch)) {
			visibleChars++;
		}
	}
	// Fill in the real level of the next line, keeping the current flags as they will be filled in later

	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
}

static const char * const luaWordListDesc[] = {
	"Keywords",
	"Basic functions",
	"String, (table) & math functions",
	"(coroutines), I/O & system facilities",
	"user1",
	"user2",
	"user3",
	"user4",
	0
};

LexerModule lmLua(SCLEX_LUA, ColouriseLuaDoc, "lua", FoldLuaDoc, luaWordListDesc);
<|endoftext|>


<commit_before>#include <iostream>

using namespace std;

/*
LeetCode 141:

    1. 判断一个链表是否有环.
    2. 如果有环，返回环的入口点位置.
*/

struct Node{
    int value;
    struct Node *next;
    Node(int x):value(x),next(NULL){}
};

// Two Point (fast & slow)
//
// Time O(n), Space O(1)
//
class Solution{

public:
    int hasCycle(Node *head)
    {
        Node *slow = head;
        Node *fast = head;

        while (fast != NULL && fast->next != NULL)
        {
            cout << slow->value << ", ";

            slow = slow->next;
            fast = fast->next->next;

            if (fast == slow)
            {
                return 1;
            }
        }

        return 0;
    }

    int entryNodeOfLinkedList(Node *head)
    {
        if (head == NULL || head->next == NULL) return -1;

        int n = 0;
        Node *slow = head;
        Node *fast = head;

        while(fast != NULL && fast->next != NULL)
        {
            cout << slow->value << ", ";
            slow = slow->next;
            fast = fast->next->next;

            if (slow == fast)     // 快、慢指针相遇(fast pointer == slow pointer)
            {
                Node *p = head;
                Node *q = slow;
                while (p != q)
                {
                    cout << p->value << ", ";
                    ++n;
                    p = p->next;
                    q = q->next;
                }

                // return p; // 返回入口节点的指针
                return n; // 第n个(n=0,1,2,3...n)
            }
        }

        return -1; // 没有环
    }

};

int main(void)
{
    Node n1(1);
    Node n2(2);
    Node n3(3);
    Node n4(4);
    Node n5(5);
    Node n6(6);
    Node n7(7);
    Node n8(8);
    Node n9(9);
    Node n10(10);
    Node n11(11);

    n1.next = &n2;
    n2.next = &n3;
    n3.next = &n4;
    n4.next = &n5;
    n5.next = &n6;
    n6.next = &n7;
    n7.next = &n8;
    n8.next = &n9;
    n9.next = &n10;
    n10.next = &n11;
    n11.next = &n5;

    Solution s;

    int has_cycle = s.hasCycle(&n1);
    cout << "has_cycle: " << has_cycle << endl;

    int entry = s.entryNodeOfLinkedList(&n1);
    cout << "entry: " << entry << endl;

    return 0;
}

<commit_msg>Modify linked list cycle<commit_after>#include <iostream>

using namespace std;

/*
LeetCode 141:

    1. 判断一个链表是否有环.
    2. 如果有环，返回环的入口点位置.
*/

struct Node{
    int value;
    struct Node *next;
    Node(int x):value(x),next(NULL){}
};

// Two Point (fast & slow)
//
// Time O(n), Space O(1)
//
class Solution{

public:
    int hasCycle(Node *head)
    {
        Node *slow = head;
        Node *fast = head;

        while (fast != NULL && fast->next != NULL)
        {
            slow = slow->next;
            fast = fast->next->next;

            if (fast == slow)
            {
                return 1;
            }
        }

        return 0;
    }

    int entryNodeOfLinkedList(Node *head)
    {
        if (head == NULL || head->next == NULL) return -1;

        int n = 0;
        Node *slow = head;
        Node *fast = head;

        while(fast != NULL && fast->next != NULL)
        {
            slow = slow->next;
            fast = fast->next->next;

            if (slow == fast)     // 快、慢指针相遇(fast pointer == slow pointer)
            {
                Node *p = head;
                Node *q = slow;
                while (p != q)
                {
                    ++n;
                    p = p->next;
                    q = q->next;
                }

                // return p; // 返回入口节点的指针
                return n; // 第n个(n=0,1,2,3...n)
            }
        }

        return -1; // 没有环
    }

};

int main(void)
{
    Node n1(1);
    Node n2(2);
    Node n3(3);
    Node n4(4);
    Node n5(5);
    Node n6(6);
    Node n7(7);
    Node n8(8);
    Node n9(9);
    Node n10(10);
    Node n11(11);

    n1.next = &n2;
    n2.next = &n3;
    n3.next = &n4;
    n4.next = &n5;
    n5.next = &n6;
    n6.next = &n7;
    n7.next = &n8;
    n8.next = &n9;
    n9.next = &n10;
    n10.next = &n11;
    n11.next = &n5;

    Solution s;

    int has_cycle = s.hasCycle(&n1);
    cout << "has_cycle: " << has_cycle << endl;

    int entry = s.entryNodeOfLinkedList(&n1);
    cout << "entry: " << entry << endl;

    return 0;
}

<|endoftext|>


<commit_before>// Scintilla source code edit control
/** @file LexSQL.cxx
 ** Lexer for SQL, including PL/SQL and SQL*Plus.
 **/
// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

static inline bool IsAWordChar(int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

static inline bool IsAWordStart(int ch) {
	return (ch < 0x80) && (isalpha(ch) || ch == '_');
}

static inline bool IsADoxygenChar(int ch) {
	return (islower(ch) || ch == '$' || ch == '@' ||
	        ch == '\\' || ch == '&' || ch == '<' ||
	        ch == '>' || ch == '#' || ch == '{' ||
	        ch == '}' || ch == '[' || ch == ']');
}

static inline bool IsANumberChar(int ch) {
	// Not exactly following number definition (several dots are seen as OK, etc.)
	// but probably enough in most cases.
	return (ch < 0x80) &&
	        (isdigit(ch) || toupper(ch) == 'E' ||
             ch == '.' || ch == '-' || ch == '+');
}

static void ColouriseSQLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
                            Accessor &styler) {

	WordList &keywords1 = *keywordlists[0];
	WordList &keywords2 = *keywordlists[1];
	WordList &kw_pldoc = *keywordlists[2];
	WordList &kw_sqlplus = *keywordlists[3];
	WordList &kw_user1 = *keywordlists[4];
	WordList &kw_user2 = *keywordlists[5];
	WordList &kw_user3 = *keywordlists[6];
	WordList &kw_user4 = *keywordlists[7];

	StyleContext sc(startPos, length, initStyle, styler);

	// property sql.backslash.escapes
	//	Enables backslash as an escape character in SQL.
	bool sqlBackslashEscapes = styler.GetPropertyInt("sql.backslash.escapes", 0) != 0;

	bool sqlBackticksIdentifier = styler.GetPropertyInt("lexer.sql.backticks.identifier", 0) != 0;
	int styleBeforeDCKeyword = SCE_SQL_DEFAULT;
	for (; sc.More(); sc.Forward()) {
		// Determine if the current state should terminate.
		switch (sc.state) {
		case SCE_SQL_OPERATOR:
			sc.SetState(SCE_SQL_DEFAULT);
			break;
		case SCE_SQL_NUMBER:
			// We stop the number definition on non-numerical non-dot non-eE non-sign char
			if (!IsANumberChar(sc.ch)) {
				sc.SetState(SCE_SQL_DEFAULT);
			}
			break;
		case SCE_SQL_IDENTIFIER:
			if (!IsAWordChar(sc.ch)) {
				int nextState = SCE_SQL_DEFAULT;
				char s[1000];
				sc.GetCurrentLowered(s, sizeof(s));
				if (keywords1.InList(s)) {
					sc.ChangeState(SCE_SQL_WORD);
				} else if (keywords2.InList(s)) {
					sc.ChangeState(SCE_SQL_WORD2);
				} else if (kw_sqlplus.InListAbbreviated(s, '~')) {
					sc.ChangeState(SCE_SQL_SQLPLUS);
					if (strncmp(s, "rem", 3) == 0) {
						nextState = SCE_SQL_SQLPLUS_COMMENT;
					} else if (strncmp(s, "pro", 3) == 0) {
						nextState = SCE_SQL_SQLPLUS_PROMPT;
					}
				} else if (kw_user1.InList(s)) {
					sc.ChangeState(SCE_SQL_USER1);
				} else if (kw_user2.InList(s)) {
					sc.ChangeState(SCE_SQL_USER2);
				} else if (kw_user3.InList(s)) {
					sc.ChangeState(SCE_SQL_USER3);
				} else if (kw_user4.InList(s)) {
					sc.ChangeState(SCE_SQL_USER4);
				}
				sc.SetState(nextState);
			}
			break;
		case SCE_SQL_QUOTEDIDENTIFIER:
			if (sc.ch == 0x60) {
				if (sc.chNext == 0x60) {
					sc.Forward();	// Ignore it
				} else {
					sc.ForwardSetState(SCE_SQL_DEFAULT);
				}
			}
			break;
		case SCE_SQL_COMMENT:
			if (sc.Match('*', '/')) {
				sc.Forward();
				sc.ForwardSetState(SCE_SQL_DEFAULT);
			}
			break;
		case SCE_SQL_COMMENTDOC:
			if (sc.Match('*', '/')) {
				sc.Forward();
				sc.ForwardSetState(SCE_SQL_DEFAULT);
			} else if (sc.ch == '@' || sc.ch == '\\') { // Doxygen support
				// Verify that we have the conditions to mark a comment-doc-keyword
				if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
					styleBeforeDCKeyword = SCE_SQL_COMMENTDOC;
					sc.SetState(SCE_SQL_COMMENTDOCKEYWORD);
				}
			}
			break;
		case SCE_SQL_COMMENTLINE:
		case SCE_SQL_COMMENTLINEDOC:
		case SCE_SQL_SQLPLUS_COMMENT:
		case SCE_SQL_SQLPLUS_PROMPT:
			if (sc.atLineStart) {
				sc.SetState(SCE_SQL_DEFAULT);
			}
			break;
		case SCE_SQL_COMMENTDOCKEYWORD:
			if ((styleBeforeDCKeyword == SCE_SQL_COMMENTDOC) && sc.Match('*', '/')) {
				sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);
				sc.Forward();
				sc.ForwardSetState(SCE_SQL_DEFAULT);
			} else if (!IsADoxygenChar(sc.ch)) {
				char s[100];
				sc.GetCurrentLowered(s, sizeof(s));
				if (!isspace(sc.ch) || !kw_pldoc.InList(s + 1)) {
					sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);
				}
				sc.SetState(styleBeforeDCKeyword);
			}
			break;
		case SCE_SQL_CHARACTER:
			if (sqlBackslashEscapes && sc.ch == '\\') {
				sc.Forward();
			} else if (sc.ch == '\'') {
				if (sc.chNext == '\"') {
					sc.Forward();
				} else {
					sc.ForwardSetState(SCE_SQL_DEFAULT);
				}
			}
			break;
		case SCE_SQL_STRING:
			if (sc.ch == '\\') {
				// Escape sequence
				sc.Forward();
			} else if (sc.ch == '\"') {
				if (sc.chNext == '\"') {
					sc.Forward();
				} else {
					sc.ForwardSetState(SCE_SQL_DEFAULT);
				}
			}
			break;
		}

		// Determine if a new state should be entered.
		if (sc.state == SCE_SQL_DEFAULT) {
			if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
				sc.SetState(SCE_SQL_NUMBER);
			} else if (IsAWordStart(sc.ch)) {
				sc.SetState(SCE_SQL_IDENTIFIER);
			} else if (sc.ch == 0x60 && sqlBackticksIdentifier) {
				sc.SetState(SCE_SQL_QUOTEDIDENTIFIER);
			} else if (sc.Match('/', '*')) {
				if (sc.Match("/**") || sc.Match("/*!")) {	// Support of Doxygen doc. style
					sc.SetState(SCE_SQL_COMMENTDOC);
				} else {
					sc.SetState(SCE_SQL_COMMENT);
				}
				sc.Forward();	// Eat the * so it isn't used for the end of the comment
			} else if (sc.Match('-', '-')) {
				// MySQL requires a space or control char after --
				// http://dev.mysql.com/doc/mysql/en/ansi-diff-comments.html
				// Perhaps we should enforce that with proper property:
//~ 			} else if (sc.Match("-- ")) {
				sc.SetState(SCE_SQL_COMMENTLINE);
			} else if (sc.ch == '#') {
				sc.SetState(SCE_SQL_COMMENTLINEDOC);
			} else if (sc.ch == '\'') {
				sc.SetState(SCE_SQL_CHARACTER);
			} else if (sc.ch == '\"') {
				sc.SetState(SCE_SQL_STRING);
			} else if (isoperator(static_cast<char>(sc.ch))) {
				sc.SetState(SCE_SQL_OPERATOR);
			}
		}
	}
	sc.Complete();
}

static bool IsStreamCommentStyle(int style) {
	return style == SCE_SQL_COMMENT ||
	       style == SCE_SQL_COMMENTDOC ||
	       style == SCE_SQL_COMMENTDOCKEYWORD ||
	       style == SCE_SQL_COMMENTDOCKEYWORDERROR;
}

// Store both the current line's fold level and the next lines in the
// level store to make it easy to pick up with each increment.
static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,
                            WordList *[], Accessor &styler) {
	bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
	bool foldOnlyBegin = styler.GetPropertyInt("fold.sql.only.begin", 0) != 0;

	unsigned int endPos = startPos + length;
	int visibleChars = 0;
	int lineCurrent = styler.GetLine(startPos);
	int levelCurrent = SC_FOLDLEVELBASE;
	if (lineCurrent > 0) {
		levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16;
	}
	int levelNext = levelCurrent;
	char chNext = styler[startPos];
	int styleNext = styler.StyleAt(startPos);
	int style = initStyle;
	bool endFound = false;
	for (unsigned int i = startPos; i < endPos; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);
		int stylePrev = style;
		style = styleNext;
		styleNext = styler.StyleAt(i + 1);
		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
		if (foldComment && IsStreamCommentStyle(style)) {
			if (!IsStreamCommentStyle(stylePrev)) {
				levelNext++;
			} else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
				// Comments don't end at end of line and the next character may be unstyled.
				levelNext--;
			}
		}
		if (foldComment && (style == SCE_SQL_COMMENTLINE)) {
			// MySQL needs -- comments to be followed by space or control char
			if ((ch == '-') && (chNext == '-')) {
				char chNext2 = styler.SafeGetCharAt(i + 2);
				char chNext3 = styler.SafeGetCharAt(i + 3);
				if (chNext2 == '{' || chNext3 == '{') {
					levelNext++;
				} else if (chNext2 == '}' || chNext3 == '}') {
					levelNext--;
				}
			}
		}
		if (style == SCE_SQL_OPERATOR) {
			if (ch == '(') {
				levelNext++;
			} else if (ch == ')') {
				levelNext--;
			}
		}
		// If new keyword (cannot trigger on elseif or nullif, does less tests)
		if (style == SCE_SQL_WORD && stylePrev != SCE_SQL_WORD) {
			const int MAX_KW_LEN = 6;	// Maximum length of folding keywords
			char s[MAX_KW_LEN + 2];
			unsigned int j = 0;
			for (; j < MAX_KW_LEN + 1; j++) {
				if (!iswordchar(styler[i + j])) {
					break;
				}
				s[j] = static_cast<char>(tolower(styler[i + j]));
			}
			if (j == MAX_KW_LEN + 1) {
				// Keyword too long, don't test it
				s[0] = '\0';
			} else {
				s[j] = '\0';
			}
			if ((!foldOnlyBegin) && (strcmp(s, "if") == 0 || strcmp(s, "loop") == 0)) {
				if (endFound) {
					// ignore
					endFound = false;
				} else {
					levelNext++;
				}
			} else if (strcmp(s, "begin") == 0) {
				levelNext++;
			} else if (strcmp(s, "end") == 0 ||
						// DROP TABLE IF EXISTS or CREATE TABLE IF NOT EXISTS
						strcmp(s, "exists") == 0) {
				endFound = true;
				levelNext--;
				if (levelNext < SC_FOLDLEVELBASE) {
					levelNext = SC_FOLDLEVELBASE;
				}
			}
		}
		if (atEOL) {
			int levelUse = levelCurrent;
			int lev = levelUse | levelNext << 16;
			if (visibleChars == 0 && foldCompact)
				lev |= SC_FOLDLEVELWHITEFLAG;
			if (levelUse < levelNext)
				lev |= SC_FOLDLEVELHEADERFLAG;
			if (lev != styler.LevelAt(lineCurrent)) {
				styler.SetLevel(lineCurrent, lev);
			}
			lineCurrent++;
			levelCurrent = levelNext;
			visibleChars = 0;
			endFound = false;
		}
		if (!isspacechar(ch)) {
			visibleChars++;
		}
	}
}

static const char * const sqlWordListDesc[] = {
	"Keywords",
	"Database Objects",
	"PLDoc",
	"SQL*Plus",
	"User Keywords 1",
	"User Keywords 2",
	"User Keywords 3",
	"User Keywords 4",
	0
};

LexerModule lmSQL(SCLEX_SQL, ColouriseSQLDoc, "sql", FoldSQLDoc, sqlWordListDesc);
<commit_msg>Support for SQL Anywhere allowing "ENDIF" to terminate a fold and an option for "EXISTS" not to.<commit_after>// Scintilla source code edit control
/** @file LexSQL.cxx
 ** Lexer for SQL, including PL/SQL and SQL*Plus.
 **/
// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

static inline bool IsAWordChar(int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

static inline bool IsAWordStart(int ch) {
	return (ch < 0x80) && (isalpha(ch) || ch == '_');
}

static inline bool IsADoxygenChar(int ch) {
	return (islower(ch) || ch == '$' || ch == '@' ||
	        ch == '\\' || ch == '&' || ch == '<' ||
	        ch == '>' || ch == '#' || ch == '{' ||
	        ch == '}' || ch == '[' || ch == ']');
}

static inline bool IsANumberChar(int ch) {
	// Not exactly following number definition (several dots are seen as OK, etc.)
	// but probably enough in most cases.
	return (ch < 0x80) &&
	        (isdigit(ch) || toupper(ch) == 'E' ||
             ch == '.' || ch == '-' || ch == '+');
}

static void ColouriseSQLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
                            Accessor &styler) {

	WordList &keywords1 = *keywordlists[0];
	WordList &keywords2 = *keywordlists[1];
	WordList &kw_pldoc = *keywordlists[2];
	WordList &kw_sqlplus = *keywordlists[3];
	WordList &kw_user1 = *keywordlists[4];
	WordList &kw_user2 = *keywordlists[5];
	WordList &kw_user3 = *keywordlists[6];
	WordList &kw_user4 = *keywordlists[7];

	StyleContext sc(startPos, length, initStyle, styler);

	// property sql.backslash.escapes
	//	Enables backslash as an escape character in SQL.
	bool sqlBackslashEscapes = styler.GetPropertyInt("sql.backslash.escapes", 0) != 0;

	bool sqlBackticksIdentifier = styler.GetPropertyInt("lexer.sql.backticks.identifier", 0) != 0;
	int styleBeforeDCKeyword = SCE_SQL_DEFAULT;
	for (; sc.More(); sc.Forward()) {
		// Determine if the current state should terminate.
		switch (sc.state) {
		case SCE_SQL_OPERATOR:
			sc.SetState(SCE_SQL_DEFAULT);
			break;
		case SCE_SQL_NUMBER:
			// We stop the number definition on non-numerical non-dot non-eE non-sign char
			if (!IsANumberChar(sc.ch)) {
				sc.SetState(SCE_SQL_DEFAULT);
			}
			break;
		case SCE_SQL_IDENTIFIER:
			if (!IsAWordChar(sc.ch)) {
				int nextState = SCE_SQL_DEFAULT;
				char s[1000];
				sc.GetCurrentLowered(s, sizeof(s));
				if (keywords1.InList(s)) {
					sc.ChangeState(SCE_SQL_WORD);
				} else if (keywords2.InList(s)) {
					sc.ChangeState(SCE_SQL_WORD2);
				} else if (kw_sqlplus.InListAbbreviated(s, '~')) {
					sc.ChangeState(SCE_SQL_SQLPLUS);
					if (strncmp(s, "rem", 3) == 0) {
						nextState = SCE_SQL_SQLPLUS_COMMENT;
					} else if (strncmp(s, "pro", 3) == 0) {
						nextState = SCE_SQL_SQLPLUS_PROMPT;
					}
				} else if (kw_user1.InList(s)) {
					sc.ChangeState(SCE_SQL_USER1);
				} else if (kw_user2.InList(s)) {
					sc.ChangeState(SCE_SQL_USER2);
				} else if (kw_user3.InList(s)) {
					sc.ChangeState(SCE_SQL_USER3);
				} else if (kw_user4.InList(s)) {
					sc.ChangeState(SCE_SQL_USER4);
				}
				sc.SetState(nextState);
			}
			break;
		case SCE_SQL_QUOTEDIDENTIFIER:
			if (sc.ch == 0x60) {
				if (sc.chNext == 0x60) {
					sc.Forward();	// Ignore it
				} else {
					sc.ForwardSetState(SCE_SQL_DEFAULT);
				}
			}
			break;
		case SCE_SQL_COMMENT:
			if (sc.Match('*', '/')) {
				sc.Forward();
				sc.ForwardSetState(SCE_SQL_DEFAULT);
			}
			break;
		case SCE_SQL_COMMENTDOC:
			if (sc.Match('*', '/')) {
				sc.Forward();
				sc.ForwardSetState(SCE_SQL_DEFAULT);
			} else if (sc.ch == '@' || sc.ch == '\\') { // Doxygen support
				// Verify that we have the conditions to mark a comment-doc-keyword
				if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
					styleBeforeDCKeyword = SCE_SQL_COMMENTDOC;
					sc.SetState(SCE_SQL_COMMENTDOCKEYWORD);
				}
			}
			break;
		case SCE_SQL_COMMENTLINE:
		case SCE_SQL_COMMENTLINEDOC:
		case SCE_SQL_SQLPLUS_COMMENT:
		case SCE_SQL_SQLPLUS_PROMPT:
			if (sc.atLineStart) {
				sc.SetState(SCE_SQL_DEFAULT);
			}
			break;
		case SCE_SQL_COMMENTDOCKEYWORD:
			if ((styleBeforeDCKeyword == SCE_SQL_COMMENTDOC) && sc.Match('*', '/')) {
				sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);
				sc.Forward();
				sc.ForwardSetState(SCE_SQL_DEFAULT);
			} else if (!IsADoxygenChar(sc.ch)) {
				char s[100];
				sc.GetCurrentLowered(s, sizeof(s));
				if (!isspace(sc.ch) || !kw_pldoc.InList(s + 1)) {
					sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);
				}
				sc.SetState(styleBeforeDCKeyword);
			}
			break;
		case SCE_SQL_CHARACTER:
			if (sqlBackslashEscapes && sc.ch == '\\') {
				sc.Forward();
			} else if (sc.ch == '\'') {
				if (sc.chNext == '\"') {
					sc.Forward();
				} else {
					sc.ForwardSetState(SCE_SQL_DEFAULT);
				}
			}
			break;
		case SCE_SQL_STRING:
			if (sc.ch == '\\') {
				// Escape sequence
				sc.Forward();
			} else if (sc.ch == '\"') {
				if (sc.chNext == '\"') {
					sc.Forward();
				} else {
					sc.ForwardSetState(SCE_SQL_DEFAULT);
				}
			}
			break;
		}

		// Determine if a new state should be entered.
		if (sc.state == SCE_SQL_DEFAULT) {
			if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
				sc.SetState(SCE_SQL_NUMBER);
			} else if (IsAWordStart(sc.ch)) {
				sc.SetState(SCE_SQL_IDENTIFIER);
			} else if (sc.ch == 0x60 && sqlBackticksIdentifier) {
				sc.SetState(SCE_SQL_QUOTEDIDENTIFIER);
			} else if (sc.Match('/', '*')) {
				if (sc.Match("/**") || sc.Match("/*!")) {	// Support of Doxygen doc. style
					sc.SetState(SCE_SQL_COMMENTDOC);
				} else {
					sc.SetState(SCE_SQL_COMMENT);
				}
				sc.Forward();	// Eat the * so it isn't used for the end of the comment
			} else if (sc.Match('-', '-')) {
				// MySQL requires a space or control char after --
				// http://dev.mysql.com/doc/mysql/en/ansi-diff-comments.html
				// Perhaps we should enforce that with proper property:
//~ 			} else if (sc.Match("-- ")) {
				sc.SetState(SCE_SQL_COMMENTLINE);
			} else if (sc.ch == '#') {
				sc.SetState(SCE_SQL_COMMENTLINEDOC);
			} else if (sc.ch == '\'') {
				sc.SetState(SCE_SQL_CHARACTER);
			} else if (sc.ch == '\"') {
				sc.SetState(SCE_SQL_STRING);
			} else if (isoperator(static_cast<char>(sc.ch))) {
				sc.SetState(SCE_SQL_OPERATOR);
			}
		}
	}
	sc.Complete();
}

static bool IsStreamCommentStyle(int style) {
	return style == SCE_SQL_COMMENT ||
	       style == SCE_SQL_COMMENTDOC ||
	       style == SCE_SQL_COMMENTDOCKEYWORD ||
	       style == SCE_SQL_COMMENTDOCKEYWORDERROR;
}

// Store both the current line's fold level and the next lines in the
// level store to make it easy to pick up with each increment.
static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,
                            WordList *[], Accessor &styler) {
	bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
	bool foldOnlyBegin = styler.GetPropertyInt("fold.sql.only.begin", 0) != 0;

	// property fold.sql.exists
	//	Enables "EXISTS" to end a fold as is started by "IF" in "DROP TABLE IF EXISTS".
	bool foldSqlExists = styler.GetPropertyInt("fold.sql.exists", 1) != 0;

	unsigned int endPos = startPos + length;
	int visibleChars = 0;
	int lineCurrent = styler.GetLine(startPos);
	int levelCurrent = SC_FOLDLEVELBASE;
	if (lineCurrent > 0) {
		levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16;
	}
	int levelNext = levelCurrent;
	char chNext = styler[startPos];
	int styleNext = styler.StyleAt(startPos);
	int style = initStyle;
	bool endFound = false;
	for (unsigned int i = startPos; i < endPos; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);
		int stylePrev = style;
		style = styleNext;
		styleNext = styler.StyleAt(i + 1);
		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
		if (foldComment && IsStreamCommentStyle(style)) {
			if (!IsStreamCommentStyle(stylePrev)) {
				levelNext++;
			} else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
				// Comments don't end at end of line and the next character may be unstyled.
				levelNext--;
			}
		}
		if (foldComment && (style == SCE_SQL_COMMENTLINE)) {
			// MySQL needs -- comments to be followed by space or control char
			if ((ch == '-') && (chNext == '-')) {
				char chNext2 = styler.SafeGetCharAt(i + 2);
				char chNext3 = styler.SafeGetCharAt(i + 3);
				if (chNext2 == '{' || chNext3 == '{') {
					levelNext++;
				} else if (chNext2 == '}' || chNext3 == '}') {
					levelNext--;
				}
			}
		}
		if (style == SCE_SQL_OPERATOR) {
			if (ch == '(') {
				levelNext++;
			} else if (ch == ')') {
				levelNext--;
			}
		}
		// If new keyword (cannot trigger on elseif or nullif, does less tests)
		if (style == SCE_SQL_WORD && stylePrev != SCE_SQL_WORD) {
			const int MAX_KW_LEN = 6;	// Maximum length of folding keywords
			char s[MAX_KW_LEN + 2];
			unsigned int j = 0;
			for (; j < MAX_KW_LEN + 1; j++) {
				if (!iswordchar(styler[i + j])) {
					break;
				}
				s[j] = static_cast<char>(tolower(styler[i + j]));
			}
			if (j == MAX_KW_LEN + 1) {
				// Keyword too long, don't test it
				s[0] = '\0';
			} else {
				s[j] = '\0';
			}
			if ((!foldOnlyBegin) && (strcmp(s, "if") == 0 || strcmp(s, "loop") == 0)) {
				if (endFound) {
					// ignore
					endFound = false;
				} else {
					levelNext++;
				}
			} else if (strcmp(s, "begin") == 0) {
				levelNext++;
			} else if ((strcmp(s, "end") == 0) ||
//						// DROP TABLE IF EXISTS or CREATE TABLE IF NOT EXISTS
						(foldSqlExists && (strcmp(s, "exists") == 0)) ||
//						//  SQL Anywhere permits IF ... ELSE ... ENDIF
//						//      will only be active if "endif" appears in the
//						//		keyword list.
						(strcmp(s, "endif") == 0)) {
				endFound = true;
				levelNext--;
				if (levelNext < SC_FOLDLEVELBASE) {
					levelNext = SC_FOLDLEVELBASE;
				}
			}
		}
		if (atEOL) {
			int levelUse = levelCurrent;
			int lev = levelUse | levelNext << 16;
			if (visibleChars == 0 && foldCompact)
				lev |= SC_FOLDLEVELWHITEFLAG;
			if (levelUse < levelNext)
				lev |= SC_FOLDLEVELHEADERFLAG;
			if (lev != styler.LevelAt(lineCurrent)) {
				styler.SetLevel(lineCurrent, lev);
			}
			lineCurrent++;
			levelCurrent = levelNext;
			visibleChars = 0;
			endFound = false;
		}
		if (!isspacechar(ch)) {
			visibleChars++;
		}
	}
}

static const char * const sqlWordListDesc[] = {
	"Keywords",
	"Database Objects",
	"PLDoc",
	"SQL*Plus",
	"User Keywords 1",
	"User Keywords 2",
	"User Keywords 3",
	"User Keywords 4",
	0
};

LexerModule lmSQL(SCLEX_SQL, ColouriseSQLDoc, "sql", FoldSQLDoc, sqlWordListDesc);
<|endoftext|>


<commit_before>#include "nova_renderer/frontend/resource_loader.hpp"

#include <utility>

#include "nova_renderer/nova_renderer.hpp"
#include "nova_renderer/util/logger.hpp"

using namespace nova::mem;

namespace nova::renderer {
    using namespace rhi;
    using namespace shaderpack;

    constexpr size_t STAGING_BUFFER_ALIGNMENT = 2048;
    constexpr size_t STAGING_BUFFER_TOTAL_MEMORY_SIZE = 8388608;

    size_t size_in_bytes(PixelFormat pixel_format);

    ResourceStorage::ResourceStorage(NovaRenderer& renderer) : renderer(renderer), device(renderer.get_engine()) {
        allocate_staging_buffer_memory();
    }

    std::optional<TextureResourceAccessor> ResourceStorage::create_texture(const std::string& name,
                                                                           const std::size_t width,
                                                                           const std::size_t height,
                                                                           const PixelFormat pixel_format,
                                                                           const void* data,
                                                                           AllocatorHandle<>& allocator) {

        TextureResource resource = {};

        resource.name = name;
        resource.width = width;
        resource.height = height;
        resource.format = pixel_format;

        const size_t pixel_size = size_in_bytes(pixel_format);

        shaderpack::TextureCreateInfo info = {};
        info.name = name;
        info.usage = ImageUsage::SampledImage;
        info.format.pixel_format = to_pixel_format_enum(pixel_format);
        info.format.dimension_type = TextureDimensionTypeEnum::Absolute;
        info.format.width = static_cast<float>(width);
        info.format.height = static_cast<float>(height);

        const std::shared_ptr<Buffer> staging_buffer = get_staging_buffer_with_size(width * height * pixel_size);

        resource.image = device->create_image(info, allocator);
        resource.image->is_dynamic = false;

        {
            CommandList* cmds = device->create_command_list(allocator, 0, QueueType::Transfer);

            ResourceBarrier initial_texture_barrier = {};
            initial_texture_barrier.resource_to_barrier = resource.image;
            initial_texture_barrier.access_before_barrier = AccessFlags::CopyRead;
            initial_texture_barrier.access_after_barrier = AccessFlags::CopyWrite;
            initial_texture_barrier.old_state = ResourceState::Undefined;
            initial_texture_barrier.new_state = ResourceState::CopyDestination;
            initial_texture_barrier.image_memory_barrier.aspect = ImageAspectFlags::Color;

            cmds->resource_barriers(PipelineStageFlags::TopOfPipe, PipelineStageFlags::Transfer, {initial_texture_barrier});
            cmds->upload_data_to_image(resource.image, width, height, pixel_size, staging_buffer.get(), data);

            ResourceBarrier final_texture_barrier = {};
            final_texture_barrier.resource_to_barrier = resource.image;
            final_texture_barrier.access_before_barrier = AccessFlags::CopyWrite;
            final_texture_barrier.access_after_barrier = AccessFlags::ShaderRead;
            final_texture_barrier.old_state = ResourceState::CopyDestination;
            final_texture_barrier.new_state = ResourceState::ShaderRead;
            final_texture_barrier.image_memory_barrier.aspect = ImageAspectFlags::Color;

            cmds->resource_barriers(PipelineStageFlags::Transfer, PipelineStageFlags::AllGraphics, {final_texture_barrier});

            Fence* upload_done_fence = device->create_fence(allocator);
            device->submit_command_list(cmds, QueueType::Transfer, upload_done_fence);

            // Be sure that the data copy is complete, so that this method doesn't return before the GPU is done with the staging buffer
            device->wait_for_fences({upload_done_fence});
            device->destroy_fences({upload_done_fence}, allocator);
        }

        textures.emplace(name, resource);

        return std::make_optional<TextureResourceAccessor>(&textures, name);
    }

    std::optional<TextureResourceAccessor> ResourceStorage::get_texture(const std::string& name) const {
#if NOVA_DEBUG
        if(textures.find(name) != textures.end()) {
            return std::make_optional<TextureResourceAccessor>(&textures, name);

        } else {
            NOVA_LOG(ERROR) << "Could not find image \"" << name << "\"";
            return {};
        }
#else
        return std::make_optional(TextureResourceAccessor(&textures, name));
#endif
    }

    std::optional<DescriptorSetWrite> ResourceStorage::get_descriptor_info_for_resource(const std::string& resource_name) {
        if(const auto& itr = textures.find(resource_name); itr != textures.end()) {
            DescriptorSetWrite write = {};
            write.type = DescriptorType::CombinedImageSampler;
            auto& resource_write = write.resources.emplace_back();

            resource_write.image_info.image = itr->second.image;
            resource_write.image_info.sampler = renderer.get_point_sampler();
            resource_write.image_info.format.pixel_format = to_pixel_format_enum(itr->second.format);
            resource_write.image_info.format.dimension_type = TextureDimensionTypeEnum::Absolute;
            resource_write.image_info.format.width = static_cast<float>(itr->second.width);
            resource_write.image_info.format.height = static_cast<float>(itr->second.height);

            return write;

        } else {
            NOVA_LOG(ERROR) << "Could not find resource named \"" << resource_name << "\"";
            return {};
        }
    }

    std::optional<TextureResourceAccessor> ResourceStorage::create_render_target(const std::string& name,
                                                                                const size_t width,
                                                                                const size_t height,
                                                                                const PixelFormat pixel_format,
                                                                                AllocatorHandle<>& allocator,
                                                                                const bool /* can_be_sampled // Not yet supported */) {
        shaderpack::TextureCreateInfo create_info;
        create_info.name = name;
        create_info.usage = ImageUsage::RenderTarget;
        create_info.format.pixel_format = to_pixel_format_enum(pixel_format);
        create_info.format.dimension_type = TextureDimensionTypeEnum::Absolute;
        create_info.format.width = static_cast<float>(width);
        create_info.format.height = static_cast<float>(height);

        auto* image = device->create_image(create_info, allocator);
        if(image) {
            // Barrier it into the correct format and return it

            image->is_dynamic = false;

            TextureResource resource = {};
            resource.name = name;
            resource.format = pixel_format;
            resource.height = height;
            resource.width = width;
            resource.image = image;

            {
                CommandList* cmds = device->create_command_list(allocator, 0, QueueType::Transfer);

                ResourceBarrier initial_texture_barrier = {};
                initial_texture_barrier.resource_to_barrier = resource.image;
                initial_texture_barrier.access_before_barrier = AccessFlags::ColorAttachmentRead;
                initial_texture_barrier.access_after_barrier = AccessFlags::ColorAttachmentWrite;
                initial_texture_barrier.old_state = ResourceState::Undefined;
                initial_texture_barrier.new_state = ResourceState::RenderTarget;
                initial_texture_barrier.image_memory_barrier.aspect = ImageAspectFlags::Color;

                cmds->resource_barriers(PipelineStageFlags::TopOfPipe,
                                        PipelineStageFlags::ColorAttachmentOutput,
                                        {initial_texture_barrier});

                Fence* upload_done_fence = device->create_fence(allocator);
                device->submit_command_list(cmds, QueueType::Transfer, upload_done_fence);

                // Be sure that the barrier is complete, so that this method doesn't return before the render target is ready to use
                device->wait_for_fences({upload_done_fence});
                device->destroy_fences({upload_done_fence}, allocator);
            }

            render_targets.emplace(name, resource);

            return std::make_optional<TextureResourceAccessor>(&render_targets, name);

        } else {
            NOVA_LOG(ERROR) << "Could not create render target " << name;
            return {};
        }
    }

    std::optional<TextureResourceAccessor> ResourceStorage::get_render_target(const std::string& name) const {
        if(render_targets.find(name) != render_targets.end()) {
            return std::make_optional<TextureResourceAccessor>(&render_targets, name);

        } else {
            return {};
        }
    }

    void ResourceStorage::destroy_render_target(const std::string& texture_name, AllocatorHandle<>& allocator) {
        if(const auto itr = render_targets.find(texture_name); itr != render_targets.end()) {
            device->destroy_texture(itr->second.image, allocator);
            render_targets.erase(itr);
        }
#if NOVA_DEBUG
        else {
            NOVA_LOG(ERROR) << "Could not delete texture  " << texture_name << ", are you sure you spelled it correctly?";
        }
#endif
    }

    void ResourceStorage::allocate_staging_buffer_memory() {
        DeviceMemory* memory = device
                                   ->allocate_device_memory(STAGING_BUFFER_TOTAL_MEMORY_SIZE,
                                                            MemoryUsage::StagingBuffer,
                                                            ObjectType::Buffer,
                                                            *staging_buffer_allocator)
                                   .value;

        auto* strat = staging_buffer_allocator->new_other_object<BlockAllocationStrategy>(renderer.get_global_allocator().get(),
                                                                                Bytes(STAGING_BUFFER_TOTAL_MEMORY_SIZE),
                                                                                STAGING_BUFFER_ALIGNMENT);

        staging_buffer_memory = staging_buffer_allocator->new_other_object<DeviceMemoryResource>(memory, strat);
    }

    std::shared_ptr<Buffer> ResourceStorage::get_staging_buffer_with_size(const size_t size) {
        const auto return_staging_buffer = [&](Buffer* buf) { staging_buffers[size].push_back(buf); };

        // Align the size so we can bin the staging buffers
        // TODO: Experiment and find a good alignment
        const auto a = size % STAGING_BUFFER_ALIGNMENT;
        const auto needed_size = STAGING_BUFFER_ALIGNMENT + a;
        const auto actual_size = size + needed_size;

        if(auto itr = staging_buffers.find(actual_size); itr != staging_buffers.end()) {
            auto& buffer_list = itr->second;
            if(!buffer_list.empty()) {
                auto* buffer = buffer_list.back();
                buffer_list.pop_back();

                // Specify a custom deleter like a boss
                return std::shared_ptr<Buffer>(buffer, return_staging_buffer);
            }
        }

        const BufferCreateInfo info = {actual_size, BufferUsage::StagingBuffer};

        Buffer* buffer = device->create_buffer(info, *staging_buffer_memory, *staging_buffer_allocator);
        return std::shared_ptr<Buffer>(buffer, return_staging_buffer);
    }

    size_t size_in_bytes(const PixelFormat pixel_format) {
        switch(pixel_format) {
            case PixelFormat::Rgba8:
                return 4;

            case PixelFormat::Rgba16F:
                return 8;

            case PixelFormat::Rgba32F:
                return 16;

            case PixelFormat::Depth32:
                return 4;

            case PixelFormat::Depth24Stencil8:
                return 4;

            default:
                return 4;
        }
    }
} // namespace nova::renderer
<commit_msg>[resources] Create staging buffer allocator<commit_after>#include "nova_renderer/frontend/resource_loader.hpp"

#include <utility>

#include "nova_renderer/nova_renderer.hpp"
#include "nova_renderer/util/logger.hpp"

using namespace nova::mem;

namespace nova::renderer {
    using namespace rhi;
    using namespace shaderpack;

    constexpr size_t STAGING_BUFFER_ALIGNMENT = 2048;
    constexpr size_t STAGING_BUFFER_TOTAL_MEMORY_SIZE = 8388608;

    size_t size_in_bytes(PixelFormat pixel_format);

    ResourceStorage::ResourceStorage(NovaRenderer& renderer) : renderer(renderer), device(renderer.get_engine()) {
        allocate_staging_buffer_memory();
    }

    std::optional<TextureResourceAccessor> ResourceStorage::create_texture(const std::string& name,
                                                                           const std::size_t width,
                                                                           const std::size_t height,
                                                                           const PixelFormat pixel_format,
                                                                           const void* data,
                                                                           AllocatorHandle<>& allocator) {

        TextureResource resource = {};

        resource.name = name;
        resource.width = width;
        resource.height = height;
        resource.format = pixel_format;

        const size_t pixel_size = size_in_bytes(pixel_format);

        shaderpack::TextureCreateInfo info = {};
        info.name = name;
        info.usage = ImageUsage::SampledImage;
        info.format.pixel_format = to_pixel_format_enum(pixel_format);
        info.format.dimension_type = TextureDimensionTypeEnum::Absolute;
        info.format.width = static_cast<float>(width);
        info.format.height = static_cast<float>(height);

        const std::shared_ptr<Buffer> staging_buffer = get_staging_buffer_with_size(width * height * pixel_size);

        resource.image = device->create_image(info, allocator);
        resource.image->is_dynamic = false;

        {
            CommandList* cmds = device->create_command_list(allocator, 0, QueueType::Transfer);

            ResourceBarrier initial_texture_barrier = {};
            initial_texture_barrier.resource_to_barrier = resource.image;
            initial_texture_barrier.access_before_barrier = AccessFlags::CopyRead;
            initial_texture_barrier.access_after_barrier = AccessFlags::CopyWrite;
            initial_texture_barrier.old_state = ResourceState::Undefined;
            initial_texture_barrier.new_state = ResourceState::CopyDestination;
            initial_texture_barrier.image_memory_barrier.aspect = ImageAspectFlags::Color;

            cmds->resource_barriers(PipelineStageFlags::TopOfPipe, PipelineStageFlags::Transfer, {initial_texture_barrier});
            cmds->upload_data_to_image(resource.image, width, height, pixel_size, staging_buffer.get(), data);

            ResourceBarrier final_texture_barrier = {};
            final_texture_barrier.resource_to_barrier = resource.image;
            final_texture_barrier.access_before_barrier = AccessFlags::CopyWrite;
            final_texture_barrier.access_after_barrier = AccessFlags::ShaderRead;
            final_texture_barrier.old_state = ResourceState::CopyDestination;
            final_texture_barrier.new_state = ResourceState::ShaderRead;
            final_texture_barrier.image_memory_barrier.aspect = ImageAspectFlags::Color;

            cmds->resource_barriers(PipelineStageFlags::Transfer, PipelineStageFlags::AllGraphics, {final_texture_barrier});

            Fence* upload_done_fence = device->create_fence(allocator);
            device->submit_command_list(cmds, QueueType::Transfer, upload_done_fence);

            // Be sure that the data copy is complete, so that this method doesn't return before the GPU is done with the staging buffer
            device->wait_for_fences({upload_done_fence});
            device->destroy_fences({upload_done_fence}, allocator);
        }

        textures.emplace(name, resource);

        return std::make_optional<TextureResourceAccessor>(&textures, name);
    }

    std::optional<TextureResourceAccessor> ResourceStorage::get_texture(const std::string& name) const {
#if NOVA_DEBUG
        if(textures.find(name) != textures.end()) {
            return std::make_optional<TextureResourceAccessor>(&textures, name);

        } else {
            NOVA_LOG(ERROR) << "Could not find image \"" << name << "\"";
            return {};
        }
#else
        return std::make_optional(TextureResourceAccessor(&textures, name));
#endif
    }

    std::optional<DescriptorSetWrite> ResourceStorage::get_descriptor_info_for_resource(const std::string& resource_name) {
        if(const auto& itr = textures.find(resource_name); itr != textures.end()) {
            DescriptorSetWrite write = {};
            write.type = DescriptorType::CombinedImageSampler;
            auto& resource_write = write.resources.emplace_back();

            resource_write.image_info.image = itr->second.image;
            resource_write.image_info.sampler = renderer.get_point_sampler();
            resource_write.image_info.format.pixel_format = to_pixel_format_enum(itr->second.format);
            resource_write.image_info.format.dimension_type = TextureDimensionTypeEnum::Absolute;
            resource_write.image_info.format.width = static_cast<float>(itr->second.width);
            resource_write.image_info.format.height = static_cast<float>(itr->second.height);

            return write;

        } else {
            NOVA_LOG(ERROR) << "Could not find resource named \"" << resource_name << "\"";
            return {};
        }
    }

    std::optional<TextureResourceAccessor> ResourceStorage::create_render_target(const std::string& name,
                                                                                const size_t width,
                                                                                const size_t height,
                                                                                const PixelFormat pixel_format,
                                                                                AllocatorHandle<>& allocator,
                                                                                const bool /* can_be_sampled // Not yet supported */) {
        shaderpack::TextureCreateInfo create_info;
        create_info.name = name;
        create_info.usage = ImageUsage::RenderTarget;
        create_info.format.pixel_format = to_pixel_format_enum(pixel_format);
        create_info.format.dimension_type = TextureDimensionTypeEnum::Absolute;
        create_info.format.width = static_cast<float>(width);
        create_info.format.height = static_cast<float>(height);

        auto* image = device->create_image(create_info, allocator);
        if(image) {
            // Barrier it into the correct format and return it

            image->is_dynamic = false;

            TextureResource resource = {};
            resource.name = name;
            resource.format = pixel_format;
            resource.height = height;
            resource.width = width;
            resource.image = image;

            {
                CommandList* cmds = device->create_command_list(allocator, 0, QueueType::Transfer);

                ResourceBarrier initial_texture_barrier = {};
                initial_texture_barrier.resource_to_barrier = resource.image;
                initial_texture_barrier.access_before_barrier = AccessFlags::ColorAttachmentRead;
                initial_texture_barrier.access_after_barrier = AccessFlags::ColorAttachmentWrite;
                initial_texture_barrier.old_state = ResourceState::Undefined;
                initial_texture_barrier.new_state = ResourceState::RenderTarget;
                initial_texture_barrier.image_memory_barrier.aspect = ImageAspectFlags::Color;

                cmds->resource_barriers(PipelineStageFlags::TopOfPipe,
                                        PipelineStageFlags::ColorAttachmentOutput,
                                        {initial_texture_barrier});

                Fence* upload_done_fence = device->create_fence(allocator);
                device->submit_command_list(cmds, QueueType::Transfer, upload_done_fence);

                // Be sure that the barrier is complete, so that this method doesn't return before the render target is ready to use
                device->wait_for_fences({upload_done_fence});
                device->destroy_fences({upload_done_fence}, allocator);
            }

            render_targets.emplace(name, resource);

            return std::make_optional<TextureResourceAccessor>(&render_targets, name);

        } else {
            NOVA_LOG(ERROR) << "Could not create render target " << name;
            return {};
        }
    }

    std::optional<TextureResourceAccessor> ResourceStorage::get_render_target(const std::string& name) const {
        if(render_targets.find(name) != render_targets.end()) {
            return std::make_optional<TextureResourceAccessor>(&render_targets, name);

        } else {
            return {};
        }
    }

    void ResourceStorage::destroy_render_target(const std::string& texture_name, AllocatorHandle<>& allocator) {
        if(const auto itr = render_targets.find(texture_name); itr != render_targets.end()) {
            device->destroy_texture(itr->second.image, allocator);
            render_targets.erase(itr);
        }
#if NOVA_DEBUG
        else {
            NOVA_LOG(ERROR) << "Could not delete texture  " << texture_name << ", are you sure you spelled it correctly?";
        }
#endif
    }

    void ResourceStorage::allocate_staging_buffer_memory() {
        staging_buffer_allocator = std::unique_ptr<AllocatorHandle<>>(renderer.get_global_allocator()->create_suballocator());

        DeviceMemory* memory = device
                                   ->allocate_device_memory(STAGING_BUFFER_TOTAL_MEMORY_SIZE,
                                                            MemoryUsage::StagingBuffer,
                                                            ObjectType::Buffer,
                                                            *staging_buffer_allocator)
                                   .value;

        auto* strat = staging_buffer_allocator->new_other_object<BlockAllocationStrategy>(renderer.get_global_allocator().get(),
                                                                                Bytes(STAGING_BUFFER_TOTAL_MEMORY_SIZE),
                                                                                STAGING_BUFFER_ALIGNMENT);

        staging_buffer_memory = staging_buffer_allocator->new_other_object<DeviceMemoryResource>(memory, strat);
    }

    std::shared_ptr<Buffer> ResourceStorage::get_staging_buffer_with_size(const size_t size) {
        const auto return_staging_buffer = [&](Buffer* buf) { staging_buffers[size].push_back(buf); };

        // Align the size so we can bin the staging buffers
        // TODO: Experiment and find a good alignment
        const auto a = size % STAGING_BUFFER_ALIGNMENT;
        const auto needed_size = STAGING_BUFFER_ALIGNMENT + a;
        const auto actual_size = size + needed_size;

        if(auto itr = staging_buffers.find(actual_size); itr != staging_buffers.end()) {
            auto& buffer_list = itr->second;
            if(!buffer_list.empty()) {
                auto* buffer = buffer_list.back();
                buffer_list.pop_back();

                // Specify a custom deleter like a boss
                return std::shared_ptr<Buffer>(buffer, return_staging_buffer);
            }
        }

        const BufferCreateInfo info = {actual_size, BufferUsage::StagingBuffer};

        Buffer* buffer = device->create_buffer(info, *staging_buffer_memory, *staging_buffer_allocator);
        return std::shared_ptr<Buffer>(buffer, return_staging_buffer);
    }

    size_t size_in_bytes(const PixelFormat pixel_format) {
        switch(pixel_format) {
            case PixelFormat::Rgba8:
                return 4;

            case PixelFormat::Rgba16F:
                return 8;

            case PixelFormat::Rgba32F:
                return 16;

            case PixelFormat::Depth32:
                return 4;

            case PixelFormat::Depth24Stencil8:
                return 4;

            default:
                return 4;
        }
    }
} // namespace nova::renderer
<|endoftext|>


<commit_before>/* Copyright (c) 2010 Stanford University
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdarg.h>
#include <sys/time.h>
#include "Logging.h"

namespace RAMCloud {

namespace TestLog {
    /**
     * The current predicate which is used to select test log entries.
     * This symbol is not exported.
     */
    bool (*predicate)(string) = 0;

    /**
     * Whether test log entries should be recorded.
     * This symbol is not exported.
     */
    bool enabled = false;

    /**
     * The current test log.
     * This symbol is not exported.
     */
    string message;

    /// Clear the contents of the test log.
    void
    clear()
    {
        message = "";
    }

    /**
     * Clear the test log and quit recording test log entries and
     * remove any predicate that was installed.
     */
    void
    disable()
    {
        clear();
        enabled = false;
        predicate = NULL;
    }

    /// Clear the test log and begin recording test log entries.
    void
    enable()
    {
        clear();
        enabled = true;
    }

    /**
     * Returns the current test log.
     *
     * \return
     *      The current test log.
     */
    string
    get()
    {
        return message;
    }

    /**
     * Don't call this directly, see TEST_LOG instead.
     *
     * Log a message to the test log for unit testing.
     *
     * \param[in] func
     *      The result of __PRETTY_FUNCTION__.
     * \param[in] format
     *      See #LOG except the string should end with a newline character.
     * \param[in] ...
     *      See #LOG.
     */
    void
    log(const char* func,
        const char* format, ...)
    {
        va_list ap;
        char line[512];

        if (!enabled || (predicate && !predicate(func)))
            return;

        if (message.length())
            message += " | ";

        snprintf(line, sizeof(line), "%s: ", func);
        message += line;

        va_start(ap, format);
        vsnprintf(line, sizeof(line), format, ap);
        va_end(ap);
        message += line;
    }

    /**
     * Install a predicate to select only the relevant test log entries.
     *
     * \param[in] pred
     *      A predicate which is passed the value of __PRETTY_FUNCTION__
     *      from the TEST_LOG call site.  The predicate should return true
     *      precisely when the test log entry for the corresponding TEST_LOG
     *      invocation should be included in the test log.
     */
    void
    setPredicate(bool (*pred)(string))
    {
        predicate = pred;
    }
} // end RAMCloud::TestLog

Logger logger(NOTICE);

/**
 * Friendly names for each #LogLevel value.
 * Keep this in sync with the LogLevel enum.
 */
static const char* logLevelNames[] = {"(none)", "ERROR", "WARNING",
                                      "NOTICE", "DEBUG"};

/**
 * Friendly names for each #LogModule value.
 * Keep this in sync with the LogModule enum.
 */
static const char* logModuleNames[] = {"default"};

/**
 * Create a new debug logger.
 * \param[in] level
 *      Messages for all modules at least as important as \a level will be
 *      logged.
 */
Logger::Logger(LogLevel level) : stream(stderr)
{
    setLogLevels(level);
}

/**
 * Set the log level for a particular module.
 * \param[in] module
 *      The module whose level to set.
 * \param[in] level
 *      Messages for \a module at least as important as \a level will be
 *      logged.
 */
void
Logger::setLogLevel(LogModule module, LogLevel level)
{
    logLevels[module] = level;
}

/**
 * Set the log level for a particular module.
 * \param[in] module
 *      The module whose level to set.
 * \param[in] level
 *      Messages for \a module at least as important as \a level will be
 *      logged. This will be clamped to a valid LogLevel if it is out of range.
 */
void
Logger::setLogLevel(LogModule module, int level)
{
    if (level < 0)
        level = 0;
    else if (level >= NUM_LOG_LEVELS)
        level = NUM_LOG_LEVELS - 1;
    setLogLevel(module, static_cast<LogLevel>(level));
}

/**
 * Change the log level by a relative amount for a particular module.
 * \param[in] module
 *      The module whose level to change.
 * \param[in] delta
 *      The amount (positive or negative) by which to change the log level
 *      of \a module. The resulting level will be clamped to a valid LogLevel
 *      if it is out of range.
 */
void
Logger::changeLogLevel(LogModule module, int delta)
{
    int level = static_cast<int>(logLevels[module]);
    setLogLevel(module, level + delta);
}

/**
 * Set the log level for all modules.
 * \param[in] level
 *      Messages for the modules at least as important as \a level will be
 *      logged.
 */
void
Logger::setLogLevels(LogLevel level)
{
    for (int i = 0; i < NUM_LOG_MODULES; i++) {
        LogModule module = static_cast<LogModule>(i);
        setLogLevel(module, level);
    }
}
/**
 * Set the log level for all modules.
 * \param[in] level
 *      Messages for all modules at least as important as \a level will be
 *      logged. This will be clamped to a valid LogLevel if it is out of range.
 */
void
Logger::setLogLevels(int level)
{
    if (level < 0)
        level = 0;
    else if (level >= NUM_LOG_LEVELS)
        level = NUM_LOG_LEVELS - 1;
    setLogLevels(static_cast<LogLevel>(level));
}

/**
 * Change the log level by a relative amount for all modules.
 * \param[in] delta
 *      The amount (positive or negative) by which to change the log level
 *      of the modules. The resulting level will be clamped to a valid LogLevel
 *      if it is out of range.
 */
void
Logger::changeLogLevels(int delta)
{
    for (int i = 0; i < NUM_LOG_MODULES; i++) {
        LogModule module = static_cast<LogModule>(i);
        changeLogLevel(module, delta);
    }
}

/**
 * Return the number of characters of __FILE__ that make up the path prefix.
 * That is, __FILE__ plus this value will be the relative path from the top
 * directory of the RAMCloud repo.
 */
static int
length__FILE__Prefix()
{
    const char* start = __FILE__;
    const char* match = strstr(__FILE__, "src/Logging.cc");
    assert(match != NULL);
    return (match - start);
}

/**
 * Log a message for the system administrator.
 * \param[in] module
 *      The module to which the message pertains.
 * \param[in] level
 *      See #LOG.
 * \param[in] file
 *      The result of __FILE__.
 * \param[in] line
 *      The result of __LINE__.
 * \param[in] format
 *      See #LOG except the string should end with a newline character.
 * \param[in] ...
 *      See #LOG.
 */
void
Logger::logMessage(LogModule module, LogLevel level,
                   const char* file, uint32_t line,
                   const char* format, ...)
{
    static int fileCharsToSkip = length__FILE__Prefix();

    va_list ap;
    struct timeval now;

    gettimeofday(&now, NULL);
    fprintf(stream, "%010u.%06u %s:%d %s %s: ",
            now.tv_sec, now.tv_usec,
            file + fileCharsToSkip, line,
            logModuleNames[module],
            logLevelNames[level]);

    va_start(ap, format);
    vfprintf(stream, format, ap);
    va_end(ap);

    fflush(stream);
}

/**
 * Format a message for use in #DIE().
 * \param[in] module
 *      See #logMessage().
 * \param[in] level
 *      See #logMessage().
 * \param[in] file
 *      See #logMessage().
 * \param[in] line
 *      See #logMessage().
 * \param[in] format
 *      See #logMessage().
 * \param[in] ...
 *      See #logMessage().
 * \return
 *      A string containing the formatted message.
 */
std::string
Logger::getMessage(LogModule module, LogLevel level,
                   const char* file, uint32_t line,
                   const char* format, ...)
{
    static int fileCharsToSkip = length__FILE__Prefix();
    std::string message;
    char buf[1024];
    va_list ap;

    snprintf(buf, sizeof(buf), "%s:%d %s %s: ",
             file + fileCharsToSkip, line,
             logModuleNames[module],
             logLevelNames[level]);
    message.append(buf);

    va_start(ap, format);
    vsnprintf(buf, sizeof(buf), format, ap);
    va_end(ap);
    message.append(buf);

    return message;
}

} // end RAMCloud

<commit_msg>Added transport log module name and static checks on log name arrays.<commit_after>/* Copyright (c) 2010 Stanford University
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdarg.h>
#include <sys/time.h>
#include "Logging.h"

namespace RAMCloud {

namespace TestLog {
    /**
     * The current predicate which is used to select test log entries.
     * This symbol is not exported.
     */
    bool (*predicate)(string) = 0;

    /**
     * Whether test log entries should be recorded.
     * This symbol is not exported.
     */
    bool enabled = false;

    /**
     * The current test log.
     * This symbol is not exported.
     */
    string message;

    /// Clear the contents of the test log.
    void
    clear()
    {
        message = "";
    }

    /**
     * Clear the test log and quit recording test log entries and
     * remove any predicate that was installed.
     */
    void
    disable()
    {
        clear();
        enabled = false;
        predicate = NULL;
    }

    /// Clear the test log and begin recording test log entries.
    void
    enable()
    {
        clear();
        enabled = true;
    }

    /**
     * Returns the current test log.
     *
     * \return
     *      The current test log.
     */
    string
    get()
    {
        return message;
    }

    /**
     * Don't call this directly, see TEST_LOG instead.
     *
     * Log a message to the test log for unit testing.
     *
     * \param[in] func
     *      The result of __PRETTY_FUNCTION__.
     * \param[in] format
     *      See #LOG except the string should end with a newline character.
     * \param[in] ...
     *      See #LOG.
     */
    void
    log(const char* func,
        const char* format, ...)
    {
        va_list ap;
        char line[512];

        if (!enabled || (predicate && !predicate(func)))
            return;

        if (message.length())
            message += " | ";

        snprintf(line, sizeof(line), "%s: ", func);
        message += line;

        va_start(ap, format);
        vsnprintf(line, sizeof(line), format, ap);
        va_end(ap);
        message += line;
    }

    /**
     * Install a predicate to select only the relevant test log entries.
     *
     * \param[in] pred
     *      A predicate which is passed the value of __PRETTY_FUNCTION__
     *      from the TEST_LOG call site.  The predicate should return true
     *      precisely when the test log entry for the corresponding TEST_LOG
     *      invocation should be included in the test log.
     */
    void
    setPredicate(bool (*pred)(string))
    {
        predicate = pred;
    }
} // end RAMCloud::TestLog

Logger logger(NOTICE);

/**
 * Friendly names for each #LogLevel value.
 * Keep this in sync with the LogLevel enum.
 */
static const char* logLevelNames[] = {"(none)", "ERROR", "WARNING",
                                      "NOTICE", "DEBUG"};

static_assert(sizeof(logLevelNames) / sizeof(logLevelNames[0]) ==
              NUM_LOG_LEVELS);
/**
 * Friendly names for each #LogModule value.
 * Keep this in sync with the LogModule enum.
 */
static const char* logModuleNames[] = {"default", "transport"};

static_assert(sizeof(logModuleNames) / sizeof(logModuleNames[0]) ==
              NUM_LOG_MODULES);

/**
 * Create a new debug logger.
 * \param[in] level
 *      Messages for all modules at least as important as \a level will be
 *      logged.
 */
Logger::Logger(LogLevel level) : stream(stderr)
{
    setLogLevels(level);
}

/**
 * Set the log level for a particular module.
 * \param[in] module
 *      The module whose level to set.
 * \param[in] level
 *      Messages for \a module at least as important as \a level will be
 *      logged.
 */
void
Logger::setLogLevel(LogModule module, LogLevel level)
{
    logLevels[module] = level;
}

/**
 * Set the log level for a particular module.
 * \param[in] module
 *      The module whose level to set.
 * \param[in] level
 *      Messages for \a module at least as important as \a level will be
 *      logged. This will be clamped to a valid LogLevel if it is out of range.
 */
void
Logger::setLogLevel(LogModule module, int level)
{
    if (level < 0)
        level = 0;
    else if (level >= NUM_LOG_LEVELS)
        level = NUM_LOG_LEVELS - 1;
    setLogLevel(module, static_cast<LogLevel>(level));
}

/**
 * Change the log level by a relative amount for a particular module.
 * \param[in] module
 *      The module whose level to change.
 * \param[in] delta
 *      The amount (positive or negative) by which to change the log level
 *      of \a module. The resulting level will be clamped to a valid LogLevel
 *      if it is out of range.
 */
void
Logger::changeLogLevel(LogModule module, int delta)
{
    int level = static_cast<int>(logLevels[module]);
    setLogLevel(module, level + delta);
}

/**
 * Set the log level for all modules.
 * \param[in] level
 *      Messages for the modules at least as important as \a level will be
 *      logged.
 */
void
Logger::setLogLevels(LogLevel level)
{
    for (int i = 0; i < NUM_LOG_MODULES; i++) {
        LogModule module = static_cast<LogModule>(i);
        setLogLevel(module, level);
    }
}
/**
 * Set the log level for all modules.
 * \param[in] level
 *      Messages for all modules at least as important as \a level will be
 *      logged. This will be clamped to a valid LogLevel if it is out of range.
 */
void
Logger::setLogLevels(int level)
{
    if (level < 0)
        level = 0;
    else if (level >= NUM_LOG_LEVELS)
        level = NUM_LOG_LEVELS - 1;
    setLogLevels(static_cast<LogLevel>(level));
}

/**
 * Change the log level by a relative amount for all modules.
 * \param[in] delta
 *      The amount (positive or negative) by which to change the log level
 *      of the modules. The resulting level will be clamped to a valid LogLevel
 *      if it is out of range.
 */
void
Logger::changeLogLevels(int delta)
{
    for (int i = 0; i < NUM_LOG_MODULES; i++) {
        LogModule module = static_cast<LogModule>(i);
        changeLogLevel(module, delta);
    }
}

/**
 * Return the number of characters of __FILE__ that make up the path prefix.
 * That is, __FILE__ plus this value will be the relative path from the top
 * directory of the RAMCloud repo.
 */
static int
length__FILE__Prefix()
{
    const char* start = __FILE__;
    const char* match = strstr(__FILE__, "src/Logging.cc");
    assert(match != NULL);
    return (match - start);
}

/**
 * Log a message for the system administrator.
 * \param[in] module
 *      The module to which the message pertains.
 * \param[in] level
 *      See #LOG.
 * \param[in] file
 *      The result of __FILE__.
 * \param[in] line
 *      The result of __LINE__.
 * \param[in] format
 *      See #LOG except the string should end with a newline character.
 * \param[in] ...
 *      See #LOG.
 */
void
Logger::logMessage(LogModule module, LogLevel level,
                   const char* file, uint32_t line,
                   const char* format, ...)
{
    static int fileCharsToSkip = length__FILE__Prefix();

    va_list ap;
    struct timeval now;

    gettimeofday(&now, NULL);
    fprintf(stream, "%010u.%06u %s:%d %s %s: ",
            now.tv_sec, now.tv_usec,
            file + fileCharsToSkip, line,
            logModuleNames[module],
            logLevelNames[level]);

    va_start(ap, format);
    vfprintf(stream, format, ap);
    va_end(ap);

    fflush(stream);
}

/**
 * Format a message for use in #DIE().
 * \param[in] module
 *      See #logMessage().
 * \param[in] level
 *      See #logMessage().
 * \param[in] file
 *      See #logMessage().
 * \param[in] line
 *      See #logMessage().
 * \param[in] format
 *      See #logMessage().
 * \param[in] ...
 *      See #logMessage().
 * \return
 *      A string containing the formatted message.
 */
std::string
Logger::getMessage(LogModule module, LogLevel level,
                   const char* file, uint32_t line,
                   const char* format, ...)
{
    static int fileCharsToSkip = length__FILE__Prefix();
    std::string message;
    char buf[1024];
    va_list ap;

    snprintf(buf, sizeof(buf), "%s:%d %s %s: ",
             file + fileCharsToSkip, line,
             logModuleNames[module],
             logLevelNames[level]);
    message.append(buf);

    va_start(ap, format);
    vsnprintf(buf, sizeof(buf), format, ap);
    va_end(ap);
    message.append(buf);

    return message;
}

} // end RAMCloud

<|endoftext|>