You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

679 lines
21 KiB

  1. // Copyright 2014 The Prometheus Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package prometheus
  14. import (
  15. "fmt"
  16. "sync"
  17. "github.com/prometheus/common/model"
  18. )
  19. // MetricVec is a Collector to bundle metrics of the same name that differ in
  20. // their label values. MetricVec is not used directly but as a building block
  21. // for implementations of vectors of a given metric type, like GaugeVec,
  22. // CounterVec, SummaryVec, and HistogramVec. It is exported so that it can be
  23. // used for custom Metric implementations.
  24. //
  25. // To create a FooVec for custom Metric Foo, embed a pointer to MetricVec in
  26. // FooVec and initialize it with NewMetricVec. Implement wrappers for
  27. // GetMetricWithLabelValues and GetMetricWith that return (Foo, error) rather
  28. // than (Metric, error). Similarly, create a wrapper for CurryWith that returns
  29. // (*FooVec, error) rather than (*MetricVec, error). It is recommended to also
  30. // add the convenience methods WithLabelValues, With, and MustCurryWith, which
  31. // panic instead of returning errors. See also the MetricVec example.
  32. type MetricVec struct {
  33. *metricMap
  34. curry []curriedLabelValue
  35. // hashAdd and hashAddByte can be replaced for testing collision handling.
  36. hashAdd func(h uint64, s string) uint64
  37. hashAddByte func(h uint64, b byte) uint64
  38. }
  39. // NewMetricVec returns an initialized metricVec.
  40. func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec {
  41. return &MetricVec{
  42. metricMap: &metricMap{
  43. metrics: map[uint64][]metricWithLabelValues{},
  44. desc: desc,
  45. newMetric: newMetric,
  46. },
  47. hashAdd: hashAdd,
  48. hashAddByte: hashAddByte,
  49. }
  50. }
  51. // DeleteLabelValues removes the metric where the variable labels are the same
  52. // as those passed in as labels (same order as the VariableLabels in Desc). It
  53. // returns true if a metric was deleted.
  54. //
  55. // It is not an error if the number of label values is not the same as the
  56. // number of VariableLabels in Desc. However, such inconsistent label count can
  57. // never match an actual metric, so the method will always return false in that
  58. // case.
  59. //
  60. // Note that for more than one label value, this method is prone to mistakes
  61. // caused by an incorrect order of arguments. Consider Delete(Labels) as an
  62. // alternative to avoid that type of mistake. For higher label numbers, the
  63. // latter has a much more readable (albeit more verbose) syntax, but it comes
  64. // with a performance overhead (for creating and processing the Labels map).
  65. // See also the CounterVec example.
  66. func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
  67. lvs = constrainLabelValues(m.desc, lvs, m.curry)
  68. h, err := m.hashLabelValues(lvs)
  69. if err != nil {
  70. return false
  71. }
  72. return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry)
  73. }
  74. // Delete deletes the metric where the variable labels are the same as those
  75. // passed in as labels. It returns true if a metric was deleted.
  76. //
  77. // It is not an error if the number and names of the Labels are inconsistent
  78. // with those of the VariableLabels in Desc. However, such inconsistent Labels
  79. // can never match an actual metric, so the method will always return false in
  80. // that case.
  81. //
  82. // This method is used for the same purpose as DeleteLabelValues(...string). See
  83. // there for pros and cons of the two methods.
  84. func (m *MetricVec) Delete(labels Labels) bool {
  85. labels = constrainLabels(m.desc, labels)
  86. h, err := m.hashLabels(labels)
  87. if err != nil {
  88. return false
  89. }
  90. return m.metricMap.deleteByHashWithLabels(h, labels, m.curry)
  91. }
  92. // DeletePartialMatch deletes all metrics where the variable labels contain all of those
  93. // passed in as labels. The order of the labels does not matter.
  94. // It returns the number of metrics deleted.
  95. //
  96. // Note that curried labels will never be matched if deleting from the curried vector.
  97. // To match curried labels with DeletePartialMatch, it must be called on the base vector.
  98. func (m *MetricVec) DeletePartialMatch(labels Labels) int {
  99. labels = constrainLabels(m.desc, labels)
  100. return m.metricMap.deleteByLabels(labels, m.curry)
  101. }
  102. // Without explicit forwarding of Describe, Collect, Reset, those methods won't
  103. // show up in GoDoc.
  104. // Describe implements Collector.
  105. func (m *MetricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) }
  106. // Collect implements Collector.
  107. func (m *MetricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) }
  108. // Reset deletes all metrics in this vector.
  109. func (m *MetricVec) Reset() { m.metricMap.Reset() }
  110. // CurryWith returns a vector curried with the provided labels, i.e. the
  111. // returned vector has those labels pre-set for all labeled operations performed
  112. // on it. The cardinality of the curried vector is reduced accordingly. The
  113. // order of the remaining labels stays the same (just with the curried labels
  114. // taken out of the sequence – which is relevant for the
  115. // (GetMetric)WithLabelValues methods). It is possible to curry a curried
  116. // vector, but only with labels not yet used for currying before.
  117. //
  118. // The metrics contained in the MetricVec are shared between the curried and
  119. // uncurried vectors. They are just accessed differently. Curried and uncurried
  120. // vectors behave identically in terms of collection. Only one must be
  121. // registered with a given registry (usually the uncurried version). The Reset
  122. // method deletes all metrics, even if called on a curried vector.
  123. //
  124. // Note that CurryWith is usually not called directly but through a wrapper
  125. // around MetricVec, implementing a vector for a specific Metric
  126. // implementation, for example GaugeVec.
  127. func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
  128. var (
  129. newCurry []curriedLabelValue
  130. oldCurry = m.curry
  131. iCurry int
  132. )
  133. for i, label := range m.desc.variableLabels {
  134. val, ok := labels[label.Name]
  135. if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
  136. if ok {
  137. return nil, fmt.Errorf("label name %q is already curried", label.Name)
  138. }
  139. newCurry = append(newCurry, oldCurry[iCurry])
  140. iCurry++
  141. } else {
  142. if !ok {
  143. continue // Label stays uncurried.
  144. }
  145. newCurry = append(newCurry, curriedLabelValue{i, label.Constrain(val)})
  146. }
  147. }
  148. if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
  149. return nil, fmt.Errorf("%d unknown label(s) found during currying", l)
  150. }
  151. return &MetricVec{
  152. metricMap: m.metricMap,
  153. curry: newCurry,
  154. hashAdd: m.hashAdd,
  155. hashAddByte: m.hashAddByte,
  156. }, nil
  157. }
  158. // GetMetricWithLabelValues returns the Metric for the given slice of label
  159. // values (same order as the variable labels in Desc). If that combination of
  160. // label values is accessed for the first time, a new Metric is created (by
  161. // calling the newMetric function provided during construction of the
  162. // MetricVec).
  163. //
  164. // It is possible to call this method without using the returned Metric to only
  165. // create the new Metric but leave it in its initial state.
  166. //
  167. // Keeping the Metric for later use is possible (and should be considered if
  168. // performance is critical), but keep in mind that Reset, DeleteLabelValues and
  169. // Delete can be used to delete the Metric from the MetricVec. In that case, the
  170. // Metric will still exist, but it will not be exported anymore, even if a
  171. // Metric with the same label values is created later.
  172. //
  173. // An error is returned if the number of label values is not the same as the
  174. // number of variable labels in Desc (minus any curried labels).
  175. //
  176. // Note that for more than one label value, this method is prone to mistakes
  177. // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
  178. // an alternative to avoid that type of mistake. For higher label numbers, the
  179. // latter has a much more readable (albeit more verbose) syntax, but it comes
  180. // with a performance overhead (for creating and processing the Labels map).
  181. //
  182. // Note that GetMetricWithLabelValues is usually not called directly but through
  183. // a wrapper around MetricVec, implementing a vector for a specific Metric
  184. // implementation, for example GaugeVec.
  185. func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
  186. lvs = constrainLabelValues(m.desc, lvs, m.curry)
  187. h, err := m.hashLabelValues(lvs)
  188. if err != nil {
  189. return nil, err
  190. }
  191. return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil
  192. }
  193. // GetMetricWith returns the Metric for the given Labels map (the label names
  194. // must match those of the variable labels in Desc). If that label map is
  195. // accessed for the first time, a new Metric is created. Implications of
  196. // creating a Metric without using it and keeping the Metric for later use
  197. // are the same as for GetMetricWithLabelValues.
  198. //
  199. // An error is returned if the number and names of the Labels are inconsistent
  200. // with those of the variable labels in Desc (minus any curried labels).
  201. //
  202. // This method is used for the same purpose as
  203. // GetMetricWithLabelValues(...string). See there for pros and cons of the two
  204. // methods.
  205. //
  206. // Note that GetMetricWith is usually not called directly but through a wrapper
  207. // around MetricVec, implementing a vector for a specific Metric implementation,
  208. // for example GaugeVec.
  209. func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
  210. labels = constrainLabels(m.desc, labels)
  211. h, err := m.hashLabels(labels)
  212. if err != nil {
  213. return nil, err
  214. }
  215. return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil
  216. }
  217. func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
  218. if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil {
  219. return 0, err
  220. }
  221. var (
  222. h = hashNew()
  223. curry = m.curry
  224. iVals, iCurry int
  225. )
  226. for i := 0; i < len(m.desc.variableLabels); i++ {
  227. if iCurry < len(curry) && curry[iCurry].index == i {
  228. h = m.hashAdd(h, curry[iCurry].value)
  229. iCurry++
  230. } else {
  231. h = m.hashAdd(h, vals[iVals])
  232. iVals++
  233. }
  234. h = m.hashAddByte(h, model.SeparatorByte)
  235. }
  236. return h, nil
  237. }
  238. func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
  239. if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
  240. return 0, err
  241. }
  242. var (
  243. h = hashNew()
  244. curry = m.curry
  245. iCurry int
  246. )
  247. for i, label := range m.desc.variableLabels {
  248. val, ok := labels[label.Name]
  249. if iCurry < len(curry) && curry[iCurry].index == i {
  250. if ok {
  251. return 0, fmt.Errorf("label name %q is already curried", label.Name)
  252. }
  253. h = m.hashAdd(h, curry[iCurry].value)
  254. iCurry++
  255. } else {
  256. if !ok {
  257. return 0, fmt.Errorf("label name %q missing in label map", label.Name)
  258. }
  259. h = m.hashAdd(h, val)
  260. }
  261. h = m.hashAddByte(h, model.SeparatorByte)
  262. }
  263. return h, nil
  264. }
  265. // metricWithLabelValues provides the metric and its label values for
  266. // disambiguation on hash collision.
  267. type metricWithLabelValues struct {
  268. values []string
  269. metric Metric
  270. }
  271. // curriedLabelValue sets the curried value for a label at the given index.
  272. type curriedLabelValue struct {
  273. index int
  274. value string
  275. }
  276. // metricMap is a helper for metricVec and shared between differently curried
  277. // metricVecs.
  278. type metricMap struct {
  279. mtx sync.RWMutex // Protects metrics.
  280. metrics map[uint64][]metricWithLabelValues
  281. desc *Desc
  282. newMetric func(labelValues ...string) Metric
  283. }
  284. // Describe implements Collector. It will send exactly one Desc to the provided
  285. // channel.
  286. func (m *metricMap) Describe(ch chan<- *Desc) {
  287. ch <- m.desc
  288. }
  289. // Collect implements Collector.
  290. func (m *metricMap) Collect(ch chan<- Metric) {
  291. m.mtx.RLock()
  292. defer m.mtx.RUnlock()
  293. for _, metrics := range m.metrics {
  294. for _, metric := range metrics {
  295. ch <- metric.metric
  296. }
  297. }
  298. }
  299. // Reset deletes all metrics in this vector.
  300. func (m *metricMap) Reset() {
  301. m.mtx.Lock()
  302. defer m.mtx.Unlock()
  303. for h := range m.metrics {
  304. delete(m.metrics, h)
  305. }
  306. }
  307. // deleteByHashWithLabelValues removes the metric from the hash bucket h. If
  308. // there are multiple matches in the bucket, use lvs to select a metric and
  309. // remove only that metric.
  310. func (m *metricMap) deleteByHashWithLabelValues(
  311. h uint64, lvs []string, curry []curriedLabelValue,
  312. ) bool {
  313. m.mtx.Lock()
  314. defer m.mtx.Unlock()
  315. metrics, ok := m.metrics[h]
  316. if !ok {
  317. return false
  318. }
  319. i := findMetricWithLabelValues(metrics, lvs, curry)
  320. if i >= len(metrics) {
  321. return false
  322. }
  323. if len(metrics) > 1 {
  324. old := metrics
  325. m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
  326. old[len(old)-1] = metricWithLabelValues{}
  327. } else {
  328. delete(m.metrics, h)
  329. }
  330. return true
  331. }
  332. // deleteByHashWithLabels removes the metric from the hash bucket h. If there
  333. // are multiple matches in the bucket, use lvs to select a metric and remove
  334. // only that metric.
  335. func (m *metricMap) deleteByHashWithLabels(
  336. h uint64, labels Labels, curry []curriedLabelValue,
  337. ) bool {
  338. m.mtx.Lock()
  339. defer m.mtx.Unlock()
  340. metrics, ok := m.metrics[h]
  341. if !ok {
  342. return false
  343. }
  344. i := findMetricWithLabels(m.desc, metrics, labels, curry)
  345. if i >= len(metrics) {
  346. return false
  347. }
  348. if len(metrics) > 1 {
  349. old := metrics
  350. m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
  351. old[len(old)-1] = metricWithLabelValues{}
  352. } else {
  353. delete(m.metrics, h)
  354. }
  355. return true
  356. }
  357. // deleteByLabels deletes a metric if the given labels are present in the metric.
  358. func (m *metricMap) deleteByLabels(labels Labels, curry []curriedLabelValue) int {
  359. m.mtx.Lock()
  360. defer m.mtx.Unlock()
  361. var numDeleted int
  362. for h, metrics := range m.metrics {
  363. i := findMetricWithPartialLabels(m.desc, metrics, labels, curry)
  364. if i >= len(metrics) {
  365. // Didn't find matching labels in this metric slice.
  366. continue
  367. }
  368. delete(m.metrics, h)
  369. numDeleted++
  370. }
  371. return numDeleted
  372. }
  373. // findMetricWithPartialLabel returns the index of the matching metric or
  374. // len(metrics) if not found.
  375. func findMetricWithPartialLabels(
  376. desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
  377. ) int {
  378. for i, metric := range metrics {
  379. if matchPartialLabels(desc, metric.values, labels, curry) {
  380. return i
  381. }
  382. }
  383. return len(metrics)
  384. }
  385. // indexOf searches the given slice of strings for the target string and returns
  386. // the index or len(items) as well as a boolean whether the search succeeded.
  387. func indexOf(target string, items []string) (int, bool) {
  388. for i, l := range items {
  389. if l == target {
  390. return i, true
  391. }
  392. }
  393. return len(items), false
  394. }
  395. // valueMatchesVariableOrCurriedValue determines if a value was previously curried,
  396. // and returns whether it matches either the "base" value or the curried value accordingly.
  397. // It also indicates whether the match is against a curried or uncurried value.
  398. func valueMatchesVariableOrCurriedValue(targetValue string, index int, values []string, curry []curriedLabelValue) (bool, bool) {
  399. for _, curriedValue := range curry {
  400. if curriedValue.index == index {
  401. // This label was curried. See if the curried value matches our target.
  402. return curriedValue.value == targetValue, true
  403. }
  404. }
  405. // This label was not curried. See if the current value matches our target label.
  406. return values[index] == targetValue, false
  407. }
  408. // matchPartialLabels searches the current metric and returns whether all of the target label:value pairs are present.
  409. func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
  410. for l, v := range labels {
  411. // Check if the target label exists in our metrics and get the index.
  412. varLabelIndex, validLabel := indexOf(l, desc.variableLabels.labelNames())
  413. if validLabel {
  414. // Check the value of that label against the target value.
  415. // We don't consider curried values in partial matches.
  416. matches, curried := valueMatchesVariableOrCurriedValue(v, varLabelIndex, values, curry)
  417. if matches && !curried {
  418. continue
  419. }
  420. }
  421. return false
  422. }
  423. return true
  424. }
  425. // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
  426. // or creates it and returns the new one.
  427. //
  428. // This function holds the mutex.
  429. func (m *metricMap) getOrCreateMetricWithLabelValues(
  430. hash uint64, lvs []string, curry []curriedLabelValue,
  431. ) Metric {
  432. m.mtx.RLock()
  433. metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs, curry)
  434. m.mtx.RUnlock()
  435. if ok {
  436. return metric
  437. }
  438. m.mtx.Lock()
  439. defer m.mtx.Unlock()
  440. metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs, curry)
  441. if !ok {
  442. inlinedLVs := inlineLabelValues(lvs, curry)
  443. metric = m.newMetric(inlinedLVs...)
  444. m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: inlinedLVs, metric: metric})
  445. }
  446. return metric
  447. }
  448. // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
  449. // or creates it and returns the new one.
  450. //
  451. // This function holds the mutex.
  452. func (m *metricMap) getOrCreateMetricWithLabels(
  453. hash uint64, labels Labels, curry []curriedLabelValue,
  454. ) Metric {
  455. m.mtx.RLock()
  456. metric, ok := m.getMetricWithHashAndLabels(hash, labels, curry)
  457. m.mtx.RUnlock()
  458. if ok {
  459. return metric
  460. }
  461. m.mtx.Lock()
  462. defer m.mtx.Unlock()
  463. metric, ok = m.getMetricWithHashAndLabels(hash, labels, curry)
  464. if !ok {
  465. lvs := extractLabelValues(m.desc, labels, curry)
  466. metric = m.newMetric(lvs...)
  467. m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: lvs, metric: metric})
  468. }
  469. return metric
  470. }
  471. // getMetricWithHashAndLabelValues gets a metric while handling possible
  472. // collisions in the hash space. Must be called while holding the read mutex.
  473. func (m *metricMap) getMetricWithHashAndLabelValues(
  474. h uint64, lvs []string, curry []curriedLabelValue,
  475. ) (Metric, bool) {
  476. metrics, ok := m.metrics[h]
  477. if ok {
  478. if i := findMetricWithLabelValues(metrics, lvs, curry); i < len(metrics) {
  479. return metrics[i].metric, true
  480. }
  481. }
  482. return nil, false
  483. }
  484. // getMetricWithHashAndLabels gets a metric while handling possible collisions in
  485. // the hash space. Must be called while holding read mutex.
  486. func (m *metricMap) getMetricWithHashAndLabels(
  487. h uint64, labels Labels, curry []curriedLabelValue,
  488. ) (Metric, bool) {
  489. metrics, ok := m.metrics[h]
  490. if ok {
  491. if i := findMetricWithLabels(m.desc, metrics, labels, curry); i < len(metrics) {
  492. return metrics[i].metric, true
  493. }
  494. }
  495. return nil, false
  496. }
  497. // findMetricWithLabelValues returns the index of the matching metric or
  498. // len(metrics) if not found.
  499. func findMetricWithLabelValues(
  500. metrics []metricWithLabelValues, lvs []string, curry []curriedLabelValue,
  501. ) int {
  502. for i, metric := range metrics {
  503. if matchLabelValues(metric.values, lvs, curry) {
  504. return i
  505. }
  506. }
  507. return len(metrics)
  508. }
  509. // findMetricWithLabels returns the index of the matching metric or len(metrics)
  510. // if not found.
  511. func findMetricWithLabels(
  512. desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
  513. ) int {
  514. for i, metric := range metrics {
  515. if matchLabels(desc, metric.values, labels, curry) {
  516. return i
  517. }
  518. }
  519. return len(metrics)
  520. }
  521. func matchLabelValues(values, lvs []string, curry []curriedLabelValue) bool {
  522. if len(values) != len(lvs)+len(curry) {
  523. return false
  524. }
  525. var iLVs, iCurry int
  526. for i, v := range values {
  527. if iCurry < len(curry) && curry[iCurry].index == i {
  528. if v != curry[iCurry].value {
  529. return false
  530. }
  531. iCurry++
  532. continue
  533. }
  534. if v != lvs[iLVs] {
  535. return false
  536. }
  537. iLVs++
  538. }
  539. return true
  540. }
  541. func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
  542. if len(values) != len(labels)+len(curry) {
  543. return false
  544. }
  545. iCurry := 0
  546. for i, k := range desc.variableLabels {
  547. if iCurry < len(curry) && curry[iCurry].index == i {
  548. if values[i] != curry[iCurry].value {
  549. return false
  550. }
  551. iCurry++
  552. continue
  553. }
  554. if values[i] != labels[k.Name] {
  555. return false
  556. }
  557. }
  558. return true
  559. }
  560. func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
  561. labelValues := make([]string, len(labels)+len(curry))
  562. iCurry := 0
  563. for i, k := range desc.variableLabels {
  564. if iCurry < len(curry) && curry[iCurry].index == i {
  565. labelValues[i] = curry[iCurry].value
  566. iCurry++
  567. continue
  568. }
  569. labelValues[i] = labels[k.Name]
  570. }
  571. return labelValues
  572. }
  573. func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
  574. labelValues := make([]string, len(lvs)+len(curry))
  575. var iCurry, iLVs int
  576. for i := range labelValues {
  577. if iCurry < len(curry) && curry[iCurry].index == i {
  578. labelValues[i] = curry[iCurry].value
  579. iCurry++
  580. continue
  581. }
  582. labelValues[i] = lvs[iLVs]
  583. iLVs++
  584. }
  585. return labelValues
  586. }
  587. func constrainLabels(desc *Desc, labels Labels) Labels {
  588. constrainedValues := make(Labels, len(labels))
  589. for l, v := range labels {
  590. if i, ok := indexOf(l, desc.variableLabels.labelNames()); ok {
  591. constrainedValues[l] = desc.variableLabels[i].Constrain(v)
  592. continue
  593. }
  594. constrainedValues[l] = v
  595. }
  596. return constrainedValues
  597. }
  598. func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) []string {
  599. constrainedValues := make([]string, len(lvs))
  600. var iCurry, iLVs int
  601. for i := 0; i < len(lvs)+len(curry); i++ {
  602. if iCurry < len(curry) && curry[iCurry].index == i {
  603. iCurry++
  604. continue
  605. }
  606. if i < len(desc.variableLabels) {
  607. constrainedValues[iLVs] = desc.variableLabels[i].Constrain(lvs[iLVs])
  608. } else {
  609. constrainedValues[iLVs] = lvs[iLVs]
  610. }
  611. iLVs++
  612. }
  613. return constrainedValues
  614. }