Register array with a field in only one of the registers in DML 1.4 - dml-lang

I want to make a register array, where one of the registers should include a field in bit 0 with a value of 1.
I have tried using a conditional without any success.
register feature_bits[i < N_FEATURE_SELECT_REGS] size 4 is unmapped {
#if (i == 1) {
field virtio_f_version_1 # [0] "VIRTIO_F_VERSION_1" {
param init_val = 1;
}
}
}
I have also tried indexing the register element and set the field accordingly
register feature_bits[i < N_FEATURE_SELECT_REGS] size 4 is unmapped;
register feature_bits[1] {
field VIRTIO_F_VERSION_1 # [0] {
param init_val = 1;
}
}
None of these approaches worked.

If we take a step back and look at what you're trying to accomplish here, it's possible that a third option can be usable. If it's only the init-value of some fields that differs, you can create a template type and iterate over it like this:
template feature_bit {
param lsb : uint64;
}
bank regs is (init, hard_reset) {
register features[i < 4] size 4 # unmapped;
method init() {
foreach f in (each feature_bit in (this)) {
features[f.lsb / 32].val[f.lsb % 32] = 1;
}
}
method hard_reset() {
init();
}
group featA is feature_bit { param lsb = 42; }
group featB is feature_bit { param lsb = 3; }
}
The init-template is depth-first recursive, so the feature registers will be initialized first, and then the init() method of the bank will run and set the value of the feature-registers by iterating over all instances of the feature_bit template. We also call init from hard_reset, which is also depth-first recursive, otherwise the register will be 0 after reset.

Arrays in DML are homogeneous; every subobject must exist for all indices. This is because when you write a method inside an array, each array index translates to an implicit method argument, so in your case, if a method in your register calls this.virtio_f_version.read(), this translates to something like regs__feature_bits__virtio_f_version_1__read(_dev, _i). This function exists for all values of i, therefore the field must exist for all values of i.
There are two approaches to solve your problem. The first is to let the field exist in all indices, and add code to make it pretend to not exist in indices other than 1:
register feature_bits[...]; {
field VIRTIO_F_VERSION_1 # [0] {
param init_val = i == 1 ? 1 : 0;
method write(uint64 value) {
if (i == 1) {
return set_f_version_1(value);
} else if (value != this.val) {
log spec_viol: "write outside fields";
}
}
}
}
The second approach is to accept that your set of registers is heterogeneous, and use a template to share code between them instead:
template feature_bit {
param i;
#if (i == 1) {
field virtio_f_version ... { ... }
}
... lots of common fields here ...
}
register feature_bits_0 is feature_bit { param index = 0; }
register feature_bits_1 is feature_bit { param index = 1; }
register feature_bits_2 is feature_bit { param index = 2; }
...
Which approach to choose is a trade-off; the first solution is more economic in terms of memory use and compile speed (because DML doesn't need to create almost-identical copies of methods across register indices), whereas the second solution gives a model that more accurately reflects the specification upon inspection (because the model will declare to the simulator that virtio_f_version_1 is a field only for one of the registers).
If you read the spec and you get the feeling that this field is a singular exception to an otherwise homogeneous array, then the first approach is probably better, but if registers vary wildly across indices then the second approach probably makes more sense.

Related

How to make some changes when calling shouldUpdate?

I want to do the following:
When the property "mobile" is changed, check to see if X is true, if so, set set a variable Y and call requestUpdate to re-render the element. The element will render something different based on variable X.
so shouldUpdate function could be:
shouldUpdate(changedProperties) {
if (changedProperties.has("mobile")) {
this._showDialog = true;
this.requestUpdate();
await this.updateComplete;
}
return this.openingDialog;
}
What's the best way to do this without using the shouldUpdate function?
You can use willUpdate to compute Y variable. willUpdate used to compute values needed during the update.
willUpdate(changedProperties) {
if (changedProperties.has("mobile")) {
this._showDialog = true;
}
}

How to update a module.exports properly?

I have this file that stores some of my environment variables.
Let's call it generalEnv.js
module.exports = {
CONSTANT_1: process.env.CONSTANT_1,
CONSTANT_2: process.env.CONSTANT_2
};
When the app initializes, I don't put the value of process.env.CONSTANT_1 in the env variables yet because I have to look into some places first if it exists(mongodb for instance). If it does not exists on mongodb, I will add a value into process.env.CONSTANT_1 and I was expecting that the value will reflect on generalEnv now.
When I tried accessing the CONSTANT_1 in another file.
Let's call it getConstantOne.js
const { CONSTANT_1 } = require('./generalEnv');
module.exports = () => {
// I was expecting that CONSTANT_1 will have a value here now
if(!CONSTANT_1) {
// do something
}
return CONSTANT_1
}
it does not reflect.. how do I update the closure of generalEnv.js for process.env.CONSTANT_1 to reflect on CONSTANT_1?
When assigning to a variable (or a value in an object/element in an array), the assignment will replace the value, not modify it. Therefore, any "copies" of that value will not be affected, and remain the same. Consider this example:
let a = 0;
let b = a;
a = 1;
What happens to b? Answer: Its value is 0.
To work around this we need some way of modifying the value instead of replacing it. Unfortunately, "primitive types" (strings/numbers/booleans etc.) cannot be modified in javascript. There are types that can be modified however, such as objects. You could solve this by wrapping your variables in an object called "env".
let env: {
CONSTANT_1: process.env.CONSTANT_1,
CONSTANT_2: process.env.CONSTANT_2
}
modules.exports = { env }
and then to modify:
env.CONSTANT_1 = "new value"
and to access:
if (!env.CONSTANT_1) { ... }

Elegant way to check if multiple strings are empty

How can I check if multiple strings are empty in an elegant way? This is how I currently do it:
//if one required field is empty, close the connection
if (registerRequest.Email == "") ||
(registerRequest.PhoneNumber == "")||
(registerRequest.NachName =="") ||
(registerRequest.VorName =="") ||
(registerRequest.Password =="") ||
(registerRequest.VerificationId ==""){
//Could not proceed
w.WriteHeader(UNABLE_TO_PROCEED)
w.Write([]byte("Unable to register account."))
return
}
Note: You may use the solution below if you keep the "is-valid" condition in your handler, and also if you separate your condition into another function or method.
You can create a simple helper function, which has a variadic parameter, and you can call it with any number of string values:
func containsEmpty(ss ...string) bool {
for _, s := range ss {
if s == "" {
return true
}
}
return false
}
Example using it:
if containsEmpty("one", "two", "") {
fmt.Println("One is empty!")
} else {
fmt.Println("All is non-empty.")
}
if containsEmpty("one", "two", "three") {
fmt.Println("One is empty!")
} else {
fmt.Println("All is non-empty.")
}
Output of the above (try it on the Go Playground):
One is empty!
All is non-empty.
Your example would look like this:
if containsEmpty(registerRequest.Email,
registerRequest.PhoneNumber,
registerRequest.NachName,
registerRequest.VorName,
registerRequest.Password,
registerRequest.VerificationId) {
// One of the listed strings is empty
}
Also registerRequest is a kinda long name, it could be shortened to like r. If you can't or don't want to rename it in the surrounding code and if you want to shorten the condition, you could also do something like this:
If registerRequest is a pointer (or interface), you could also write:
if r := registerRequest; containsEmpty(r.Email,
r.PhoneNumber,
r.NachName,
r.VorName,
r.Password,
r.VerificationId) {
// One of the listed strings is empty
}
Actually you can do this even if registerRequest is not a pointer, but then the struct will be copied. If registerRequest is a struct, then you can take its address to avoid having to copy it like this:
if r := &registerRequest; containsEmpty(r.Email,
r.PhoneNumber,
r.NachName,
r.VorName,
r.Password,
r.VerificationId) {
// One of the listed strings is empty
}
As Mario Santini mentioned in comment, a way to increase testability, encapsulate this logic, and decouple it from your handler method (which judging by the number of fields looks like it is at risk of changing at a different rate than your handler) could be to put this logic in a function:
func validRequest(registerRequest ?) bool {
return registerRequest.Email == "" ||
registerRequest.PhoneNumber == "" ||
registerRequest.NachName == "" ||
registerRequest.VorName == "" ||
registerRequest.Password == "" ||
registerRequest.VerificationId == ""
}
This now supports very focused, table driven tests, that can exercise what it means to be a valid request independent of any method involving writing headers.
It allows you to verify the valid/invalid path of your enclosing function, but to have very focused tests here. It also allows you to change what it means to be a valid request and verify it independent of your enclosing function.
You can use a switch:
switch "" {
case registerRequest.Email,
registerRequest.NachName,
registerRequest.Password,
registerRequest.PhoneNumber,
registerRequest.VerificationId,
registerRequest.VorName:
w.WriteHeader(UNABLE_TO_PROCEED)
w.Write([]byte("Unable to register account."))
return
}
https://golang.org/ref/spec#Switch_statements

Mongoose ValidationError Path required that is out of subdocument array bounds

This error is super wacky. It doesn't always fail, but when it does it looks like this.
I have some code that changes the "code" (a one character string) of elements in a subdocument array. It goes through each goal, checks to see if there's a change to be applied, and if so, applies it.
for (i = 0; i < user.goals.length; i++) {
if (transformsMap[user.goals[i].code]) {
user.goals[i].code = transformsMap[user.goals[i].code]
}
}
user.goals.sort(function (a,b) {return a.code.charCodeAt(0) - b.code.charCodeAt(0))
When I save it, sometimes I get an error like this:
'goals.3.code':
{ [ValidatorError: Path `code` is required.]
...but 3 in this case, is the length of the goals array. ie there is no goals.3 subdocument. I've tried logging user.goals and user.goals.length right before validation and they both agree that there are only 3 elements in the array.
I am totally baffled.
How about adding more checks? And also setting a default value if the if clause fails?
for (i = 0; i < user.goals.length; i++) {
if (user.goals[i] && user.goals[i].code && transformsMap[user.goals[i].code]){
user.goals[i].code = transformsMap[user.goals[i].code]
} else {
user.goals[i].code = "" // Whatever this is <-- default value.
}
}
user.goals.sort(function (a,b) {return a.code.charCodeAt(0) - b.code.charCodeAt(0))

Map/Reduce differences between Couchbase & CloudAnt

I've been playing around with Couchbase Server and now just tried replicating my local db to Cloudant, but am getting conflicting results for my map/reduce function pair to build a set of unique tags with their associated projects...
// map.js
function(doc) {
if (doc.tags) {
for(var t in doc.tags) {
emit(doc.tags[t], doc._id);
}
}
}
// reduce.js
function(key,values,rereduce) {
if (!rereduce) {
var res=[];
for(var v in values) {
res.push(values[v]);
}
return res;
} else {
return values.length;
}
}
In Cloudbase server this returns JSON like:
{"rows":[
{"key":"3d","value":["project1","project3","project8","project10"]},
{"key":"agents","value":["project2"]},
{"key":"fabrication","value":["project3","project5"]}
]}
That's exactly what I wanted & expected. However, the same query on the Cloudant replica, returns this:
{"rows":[
{"key":"3d","value":4},
{"key":"agents","value":1},
{"key":"fabrication","value":2}
]}
So it somehow only returns the length of the value array... Highly confusing & am grateful for any insights by some M&R ninjas... ;)
It looks like this is exactly the behavior you would expect given your reduce function. The key part is this:
else {
return values.length;
}
In Cloudant, rereduce is always called (since the reduce needs to span over multiple shards.) In this case, rereduce calls values.length, which will only return the length of the array.
I prefer to reduce/re-reduce implicitly rather than depending on the rereduce parameter.
function(doc) { // map
if (doc.tags) {
for(var t in doc.tags) {
emit(doc.tags[t], {id:doc._id, tag:doc.tags[t]});
}
}
}
Then reduce checks whether it is accumulating document ids from the identical tag, or whether it is just counting different tags.
function(keys, vals, rereduce) {
var initial_tag = vals[0].tag;
return vals.reduce(function(state, val) {
if(initial_tag && val.tag === initial_tag) {
// Accumulate ids which produced this tag.
var ids = state.ids;
if(!ids)
ids = [ state.id ]; // Build initial list from the state's id.
return { tag: val.tag,
, ids: ids.concat([val.id])
};
} else {
var state_count = state.ids ? state.ids.length : state;
var val_count = val.ids ? val.ids.length : val;
return state_count + val_count;
}
})
}
(I didn't test this code, but you get the idea. As long as the tag value is the same, it doesn't matter whether it's a reduce or rereduce. Once different tags start reducing together, it detects that because the tag value will change. So at that point just start accumulating.
I have used this trick before, although IMO it's rarely worth it.
Also in your specific case, this is a dangerous reduce function. You are building a wide list to see all the docs that have a tag. CouchDB likes tall lists, not fat lists. If you want to see all the docs that have a tag, you could map them.
for(var a = 0; a < doc.tags.length; a++) {
emit(doc.tags[a], doc._id);
}
Now you can query /db/_design/app/_view/docs_by_tag?key="3d" and you should get
{"total_rows":287,"offset":30,"rows":[
{"id":"project1","key":"3d","value":"project1"}
{"id":"project3","key":"3d","value":"project3"}
{"id":"project8","key":"3d","value":"project8"}
{"id":"project10","key":"3d","value":"project10"}
]}

Resources