-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
198 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--- | ||
--- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
...ipt-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/StringStore.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package software.amazon.smithy.typescript.codegen.util; | ||
|
||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.LinkedList; | ||
import java.util.Map; | ||
import java.util.Queue; | ||
import java.util.Set; | ||
import java.util.TreeMap; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
/** | ||
* Intended for use at the | ||
* {@link software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator.GenerationContext} | ||
* level, this class allocates and tracks variables assigned to string literals, allowing a | ||
* form of compression on long protocol serde files. | ||
*/ | ||
public class StringStore { | ||
private final ConcurrentHashMap<String, String> literalToVariable = new ConcurrentHashMap<>(); | ||
private final ConcurrentHashMap<String, String> variableToLiteral = new ConcurrentHashMap<>(); | ||
private final Set<String> writelog = new HashSet<>(); | ||
|
||
/** | ||
* @param literal - a literal string value. | ||
* @return the variable name assigned for that string, which may have been encountered before. | ||
*/ | ||
public String var(String literal) { | ||
if (literal == null) { | ||
throw new RuntimeException("Literal must not be null."); | ||
} | ||
if (literalToVariable.containsKey(literal)) { | ||
return literalToVariable.get(literal); | ||
} | ||
literalToVariable.put(literal, this.assignKey(literal)); | ||
return literalToVariable.get(literal); | ||
} | ||
|
||
/** | ||
* Outputs the generated code for any constants that have been | ||
* allocated but not yet retrieved. | ||
*/ | ||
public String getIncremental() { | ||
TreeMap<String, String> map = new TreeMap<>(variableToLiteral); | ||
|
||
StringBuilder sourceCode = new StringBuilder(); | ||
Set<String> incrementalKeys = new HashSet<>(); | ||
|
||
for (Map.Entry<String, String> entry : map.entrySet()) { | ||
String v = entry.getKey(); | ||
String l = entry.getValue(); | ||
if (writelog.contains(v)) { | ||
// sourceCode.append(String.format("// const %s = \"%s\";%n", v, l)); | ||
} else { | ||
incrementalKeys.add(v); | ||
sourceCode.append(String.format("const %s = \"%s\";%n", v, l)); | ||
} | ||
} | ||
|
||
writelog.addAll(incrementalKeys); | ||
|
||
return sourceCode.toString(); | ||
} | ||
|
||
/** | ||
* Assigns a new variable or returns the existing variable for a given string literal. | ||
*/ | ||
private String assignKey(String literal) { | ||
if (literalToVariable.containsKey(literal)) { | ||
return literalToVariable.get(literal); | ||
} | ||
String variable = allocateVariable(literal); | ||
variableToLiteral.put(variable, literal); | ||
literalToVariable.put(literal, variable); | ||
return variable; | ||
} | ||
|
||
/** | ||
* Assigns a unique variable using the letters from the literal. | ||
* Prefers the uppercase or word-starting letters. | ||
*/ | ||
private String allocateVariable(String literal) { | ||
String[] sections = literal.split("[-_\\s]"); | ||
StringBuilder v = new StringBuilder("_"); | ||
Queue<Character> deconfliction = new LinkedList<>(); | ||
if (sections.length > 1) { | ||
Arrays.stream(sections) | ||
.map(s -> s.charAt(0)) | ||
.filter(this::isAllowedChar) | ||
.forEach(v::append); | ||
} else { | ||
for (int i = 0; i < literal.length(); ++i) { | ||
char c = literal.charAt(i); | ||
if ((c >= 'A' && c <= 'Z') || (isNeutral(v.toString()) && isAllowedChar(c))) { | ||
v.append(c); | ||
} else if (isAllowedChar(c)) { | ||
deconfliction.add(c); | ||
} | ||
} | ||
} | ||
if (v.isEmpty()) { | ||
v.append("v"); | ||
} | ||
while (variableToLiteral.containsKey(v.toString())) { | ||
if (!deconfliction.isEmpty()) { | ||
v.append(deconfliction.poll()); | ||
} else { | ||
v.append('_'); | ||
} | ||
} | ||
return v.toString(); | ||
} | ||
|
||
/** | ||
* char is in A-Za-z. | ||
*/ | ||
private boolean isAllowedChar(char c) { | ||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); | ||
} | ||
|
||
/** | ||
* @return true if the variable has only underscores. | ||
*/ | ||
private boolean isNeutral(String variable) { | ||
return variable.chars().allMatch(c -> c == '_'); | ||
} | ||
} |